1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 12:37:49 +00:00

Merge branch 'master' into run_ucmd_as_root

This commit is contained in:
Jan Scheer 2021-11-21 19:13:13 +01:00
commit 8759fcf03a
No known key found for this signature in database
GPG key ID: C62AD4C29E2B9828
323 changed files with 39706 additions and 2831 deletions

View file

@ -2,10 +2,10 @@ name: CICD
# spell-checker:ignore (acronyms) CICD MSVC musl # spell-checker:ignore (acronyms) CICD MSVC musl
# spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic RUSTDOCFLAGS RUSTFLAGS Zpanic # spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic RUSTDOCFLAGS RUSTFLAGS Zpanic
# spell-checker:ignore (jargon) SHAs deps softprops toolchain # spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain
# spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy # spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs # spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend runtest tempfile testsuite uutils # spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend pell runtest tempfile testsuite uutils
# ToDO: [2021-06; rivy] change from `cargo-tree` to `cargo tree` once MSRV is >= 1.45 # ToDO: [2021-06; rivy] change from `cargo-tree` to `cargo tree` once MSRV is >= 1.45
@ -14,18 +14,27 @@ env:
PROJECT_DESC: "Core universal (cross-platform) utilities" PROJECT_DESC: "Core universal (cross-platform) utilities"
PROJECT_AUTH: "uutils" PROJECT_AUTH: "uutils"
RUST_MIN_SRV: "1.47.0" ## MSRV v1.47.0 RUST_MIN_SRV: "1.47.0" ## MSRV v1.47.0
# * style job configuration
STYLE_FAIL_ON_FAULT: true ## (bool) fail the build if a style job contains a fault (error or warning); may be overridden on a per-job basis
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
code_deps: style_deps:
name: Style/dependencies ## ToDO: [2021-11-10; rivy] 'Style/deps' needs more informative output and better integration of results into the GHA dashboard
name: Style/deps
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
# env:
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
job: job:
- { os: ubuntu-latest , features: feat_os_unix } # note: `cargo-udeps` panics when processing stdbuf/libstdbuf ("uu_stdbuf_libstdbuf"); either b/c of the 'cpp' crate or 'libstdbuf' itself
# ... b/c of the panic, a more limited feature set is tested (though only excluding `stdbuf`)
- { os: ubuntu-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" }
- { os: macos-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" }
- { os: windows-latest , features: feat_os_windows }
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Initialize workflow variables - name: Initialize workflow variables
@ -34,27 +43,50 @@ jobs:
run: | run: |
## VARs setup ## VARs setup
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.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; }
# failure mode
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
outputs FAIL_ON_FAULT FAULT_TYPE
# target-specific options # target-specific options
# * CARGO_FEATURES_OPTION # * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ; CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
outputs CARGO_FEATURES_OPTION outputs CARGO_FEATURES_OPTION
## note: requires 'nightly' toolchain b/c `cargo-udeps` uses the `rustc` '-Z save-analysis' option
## * ... ref: <https://github.com/est31/cargo-udeps/issues/73>
- name: Install `rust` toolchain - name: Install `rust` toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: nightly
default: true default: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal
- name: "`cargo update` testing" - name: Install `cargo-udeps`
uses: actions-rs/install@v0.1
with:
crate: cargo-udeps
version: latest
use-tool-cache: false
env:
RUSTUP_TOOLCHAIN: stable
- name: Detect unused dependencies
shell: bash shell: bash
run: | run: |
## `cargo update` testing ## Detect unused dependencies
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message> unset fault
cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::'Cargo.lock' file requires update (use \`cargo +${{ env.RUST_MIN_SRV }} update\`)" ; exit 1 ; } fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
#
cargo +nightly udeps ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all-targets &> udeps.log || cat udeps.log
grep --ignore-case "all deps seem to have been used" udeps.log || { printf "%s\n" "::${fault_type} ::${fault_prefix}: \`cargo udeps\`: style violation (unused dependency found)" ; fault=true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
code_format: style_format:
name: Style/format name: Style/format
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
# env:
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -68,6 +100,12 @@ jobs:
run: | run: |
## VARs setup ## VARs setup
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.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; }
# failure mode
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
outputs FAIL_ON_FAULT FAULT_TYPE
# target-specific options # target-specific options
# * CARGO_FEATURES_OPTION # * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ; CARGO_FEATURES_OPTION='' ;
@ -80,38 +118,147 @@ jobs:
default: true default: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
components: rustfmt components: rustfmt
- name: "`fmt` testing" - name: "`cargo fmt` testing"
shell: bash shell: bash
run: | run: |
## `fmt` testing ## `cargo 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> unset fault
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 ; } fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
- name: "`fmt` testing of tests" fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# * convert any errors/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" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt -- \"\1\"\`)/p" ; fault=true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
- name: "`cargo fmt` testing of integration tests"
if: success() || failure() # run regardless of prior step success/failure if: success() || failure() # run regardless of prior step success/failure
shell: bash shell: bash
run: | run: |
## `fmt` testing of tests ## `cargo fmt` testing of integration 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> unset fault
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 ; } fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# 'tests' is the standard/usual integration test directory
if [ -d tests ]; then
# * convert any errors/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" ; printf "%s\n" "$S" | sed -E -n "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt \"\1\"\`)/p" ; fault=true ; }
fi
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
code_lint: style_lint:
name: Style/lint name: Style/lint
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
# env:
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
job: job:
- { os: ubuntu-latest } - { os: ubuntu-latest , features: feat_os_unix }
- { os: macos-latest , features: feat_os_macos } - { os: macos-latest , features: feat_os_macos }
- { os: windows-latest , features: feat_os_windows } - { os: windows-latest , features: feat_os_windows }
steps: steps:
- uses: actions/checkout@v2 - 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; }
# failure mode
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
outputs FAIL_ON_FAULT FAULT_TYPE
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='--all-features' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
outputs CARGO_FEATURES_OPTION
# * determine sub-crate utility list
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
echo UTILITY_LIST=${UTILITY_LIST}
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
outputs CARGO_UTILITY_LIST_OPTIONS
- name: Install/setup prerequisites - name: Install/setup prerequisites
shell: bash shell: bash
run: | run: |
case '${{ matrix.job.os }}' in case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for show-utils.sh macos-latest) brew install coreutils ;; # needed for show-utils.sh
esac esac
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy
- name: "`cargo clippy` lint testing"
shell: bash
run: |
## `cargo clippy` lint testing
unset fault
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
S=$(cargo clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
style_spellcheck:
name: Style/spelling
runs-on: ${{ matrix.job.os }}
# env:
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
strategy:
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; }
# failure mode
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
outputs FAIL_ON_FAULT FAULT_TYPE
- name: Install/setup prerequisites
shell: bash
run: |
## Install/setup prerequisites
# * pin installed cspell to v4.2.8 (cspell v5+ is broken for NodeJS < v12)
## maint: [2021-11-10; rivy] `cspell` version may be advanced to v5 when used with NodeJS >= v12
sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell@4.2.8 -g ;
- name: Run `cspell`
shell: bash
run: |
## Run `cspell`
unset fault
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# * find cspell configuration ; note: avoid quotes around ${cfg_file} b/c `cspell` (v4) doesn't correctly dequote the config argument (or perhaps a subshell expansion issue?)
cfg_files=($(shopt -s nullglob ; echo {.vscode,.}/{,.}c[sS]pell{.json,.config{.js,.cjs,.json,.yaml,.yml},.yaml,.yml} ;))
cfg_file=${cfg_files[0]}
unset CSPELL_CFG_OPTION ; if [ -n "$cfg_file" ]; then CSPELL_CFG_OPTION="--config $cfg_file" ; fi
# * `cspell`
## maint: [2021-11-10; rivy] the `--no-progress` option for `cspell` is a `cspell` v5+ option
# S=$(cspell ${CSPELL_CFG_OPTION} --no-summary --no-progress "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; }
S=$(cspell ${CSPELL_CFG_OPTION} --no-summary "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
min_version:
name: MinRustV # Minimum supported rust version (aka, MinSRV or MSRV)
runs-on: ${{ matrix.job.os }}
strategy:
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v2
- name: Initialize workflow variables - name: Initialize workflow variables
id: vars id: vars
shell: bash shell: bash
@ -120,57 +267,9 @@ jobs:
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; } outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
# target-specific options # target-specific options
# * CARGO_FEATURES_OPTION # * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='--all-features' ; unset CARGO_FEATURES_OPTION
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features ${{ matrix.job.features }}' ; fi if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
outputs CARGO_FEATURES_OPTION outputs CARGO_FEATURES_OPTION
# * determine sub-crate utility list
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
echo UTILITY_LIST=${UTILITY_LIST}
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
outputs CARGO_UTILITY_LIST_OPTIONS
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy
- name: "`clippy` lint testing"
shell: bash
run: |
## `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 ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} ${{ 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
runs-on: ${{ matrix.job.os }}
strategy:
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v2
- name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }}) - name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
@ -208,25 +307,25 @@ jobs:
cargo-tree tree -V cargo-tree tree -V
# dependencies # dependencies
echo "## dependency list" echo "## dependency list"
cargo fetch --locked --quiet
## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors ## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors
RUSTUP_TOOLCHAIN=stable cargo-tree tree --locked --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique RUSTUP_TOOLCHAIN=stable cargo fetch --locked --quiet
RUSTUP_TOOLCHAIN=stable cargo-tree tree --all --locked --no-dev-dependencies --no-indent ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} | grep -vE "$PWD" | sort --unique
- name: Test - name: Test
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --features "feat_os_unix" -p uucore -p coreutils args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -p uucore -p coreutils
env: env:
RUSTFLAGS: '-Awarnings' RUSTFLAGS: "-Awarnings"
build_makefile: deps:
name: Build/Makefile name: Dependencies
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
job: job:
- { os: ubuntu-latest } - { os: ubuntu-latest , features: feat_os_unix }
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install `rust` toolchain - name: Install `rust` toolchain
@ -235,11 +334,35 @@ jobs:
toolchain: stable toolchain: stable
default: true default: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
- name: "`cargo update` testing"
shell: bash
run: |
## `cargo update` testing
# * convert any errors/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 ; }
build_makefile:
name: Build/Makefile
needs: [ min_version, deps ]
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v2
- name: Install/setup prerequisites - name: Install/setup prerequisites
shell: bash shell: bash
run: | run: |
## Install/setup prerequisites ## Install/setup prerequisites
sudo apt-get -y update ; sudo apt-get -y install python3-sphinx ; sudo apt-get -y update ; sudo apt-get -y install python3-sphinx ;
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
profile: minimal # minimal component installation (ie, no documentation)
- name: "`make build`" - name: "`make build`"
shell: bash shell: bash
run: | run: |
@ -251,13 +374,14 @@ jobs:
build: build:
name: Build name: Build
needs: [ min_version, deps ]
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
job: job:
# { os, target, cargo-options, features, use-cross, toolchain } # { os , target , cargo-options , features , use-cross , toolchain }
- { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , features: feat_os_unix_gnueabihf , use-cross: use-cross } - { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf, features: feat_os_unix_gnueabihf, use-cross: use-cross, }
- { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf , use-cross: use-cross } - { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf , use-cross: use-cross }
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
# - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_selinux , use-cross: use-cross } # - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_selinux , use-cross: use-cross }
@ -274,17 +398,6 @@ jobs:
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows } - { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install/setup prerequisites
shell: bash
run: |
## 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 ;;
esac
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
esac
- name: Initialize workflow variables - name: Initialize workflow variables
id: vars id: vars
shell: bash shell: bash
@ -373,6 +486,17 @@ jobs:
*-pc-windows-msvc) STRIP="" ;; *-pc-windows-msvc) STRIP="" ;;
esac; esac;
outputs STRIP outputs STRIP
- name: Install/setup prerequisites
shell: bash
run: |
## 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 ;;
esac
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
esac
- name: Create all needed build/work directories - name: Create all needed build/work directories
shell: bash shell: bash
run: | run: |
@ -380,12 +504,23 @@ jobs:
mkdir -p '${{ steps.vars.outputs.STAGING }}' mkdir -p '${{ steps.vars.outputs.STAGING }}'
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}' mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}'
mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg' mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg'
- name: Install/setup prerequisites
shell: bash
run: |
## 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 ;;
esac
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
esac
- name: rust toolchain ~ install - name: rust toolchain ~ install
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
env: # env:
# Override auto-detection of RAM for Rustc install. # # Override auto-detection of RAM for Rustc install.
# https://github.com/rust-lang/rustup/issues/2229#issuecomment-585855925 # # https://github.com/rust-lang/rustup/issues/2229#issuecomment-585855925
RUSTUP_UNPACK_RAM: "21474836480" # RUSTUP_UNPACK_RAM: "21474836480"
with: with:
toolchain: ${{ steps.vars.outputs.TOOLCHAIN }} toolchain: ${{ steps.vars.outputs.TOOLCHAIN }}
target: ${{ matrix.job.target }} target: ${{ matrix.job.target }}
@ -502,6 +637,7 @@ jobs:
test_busybox: test_busybox:
name: Tests/BusyBox test suite name: Tests/BusyBox test suite
needs: [ min_version, deps ]
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
fail-fast: false fail-fast: false
@ -510,16 +646,17 @@ jobs:
- { os: ubuntu-latest } - { os: ubuntu-latest }
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install/setup prerequisites
shell: bash
run: |
## Install/setup prerequisites
make prepare-busytest
- name: Install `rust` toolchain - name: Install `rust` toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
default: true default: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
- name: Install/setup prerequisites
shell: bash
run: |
make prepare-busytest
- name: "Run BusyBox test suite" - name: "Run BusyBox test suite"
shell: bash shell: bash
run: | run: |
@ -532,53 +669,75 @@ jobs:
if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi
test_freebsd: test_freebsd:
runs-on: macos-latest
name: Tests/FreeBSD test suite name: Tests/FreeBSD test suite
needs: [ min_version, deps ]
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: macos-10.15 , features: unix } ## GHA MacOS-11.0 VM won't have VirtualBox; refs: <https://github.com/actions/virtual-environments/issues/4060> , <https://github.com/actions/virtual-environments/pull/4010>
env: env:
mem: 2048 mem: 2048
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Prepare, build and test - name: Prepare, build and test
id: test ## spell-checker:ignore (ToDO) sshfs usesh vmactions
uses: vmactions/freebsd-vm@v0.1.5 uses: vmactions/freebsd-vm@v0.1.5
with: with:
usesh: true usesh: true
# sync: sshfs
prepare: pkg install -y curl gmake sudo prepare: pkg install -y curl gmake sudo
run: | run: |
# Need to be run in the same block. Otherwise, we are back on the mac host. ## Prepare, build, and test
# implementation modelled after ref: <https://github.com/rust-lang/rustup/pull/2783>
# * NOTE: All steps need to be run in this block, otherwise, we are operating back on the mac host
set -e set -e
pw adduser -n cuuser -d /root/ -g wheel -c "Coreutils user to build" -w random #
chown -R cuuser:wheel /root/ /Users/runner/work/coreutils/ TEST_USER=tester
REPO_NAME=${GITHUB_WORKSPACE##*/}
WORKSPACE_PARENT="/Users/runner/work/${REPO_NAME}"
WORKSPACE="${WORKSPACE_PARENT}/${REPO_NAME}"
#
pw adduser -n ${TEST_USER} -d /root/ -g wheel -c "Coreutils user to build" -w random
# chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/
chown -R ${TEST_USER}:wheel /root/ "/Users/runner/work/${REPO_NAME}"/
whoami whoami
#
# Needs to be done in a sudo as we are changing users # Further work needs to be done in a sudo as we are changing users
sudo -i -u cuuser sh << EOF sudo -i -u ${TEST_USER} sh << EOF
set -e set -e
whoami whoami
curl https://sh.rustup.rs -sSf --output rustup.sh curl https://sh.rustup.rs -sSf --output rustup.sh
sh rustup.sh -y --profile=minimal sh rustup.sh -y --profile=minimal
. $HOME/.cargo/env
## Info ## Info
# environment # environment
echo "## environment" echo "## environment"
echo "CI='${CI}'" echo "CI='${CI}'"
# tooling info display echo "REPO_NAME='${REPO_NAME}'"
echo "## tooling" echo "TEST_USER='${TEST_USER}'"
. $HOME/.cargo/env echo "WORKSPACE_PARENT='${WORKSPACE_PARENT}'"
echo "WORKSPACE='${WORKSPACE}'"
env | sort
# tooling info
echo "## tooling info"
cargo -V cargo -V
rustc -V rustc -V
env #
cd "${WORKSPACE}"
# where the files are resynced unset FAULT
cd /Users/runner/work/coreutils/coreutils/ cargo build || FAULT=1
cargo build cargo test --features "${{ matrix.job.features }}" || FAULT=1
cargo test --features feat_os_unix -p uucore -p coreutils cargo test --features "${{ matrix.job.features }}" -p uucore || FAULT=1
# Clean to avoid to rsync back the files # Clean to avoid to rsync back the files
cargo clean cargo clean
if (test -n "$FAULT"); then exit 1 ; fi
EOF EOF
coverage: coverage:
name: Code Coverage name: Code Coverage
needs: build
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
fail-fast: true fail-fast: true
@ -590,13 +749,6 @@ jobs:
- { os: windows-latest , features: windows } - { os: windows-latest , features: windows }
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install/setup prerequisites
shell: bash
run: |
## Install/setup prerequisites
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
esac
# - name: Reattach HEAD ## may be needed for accurate code coverage info # - name: Reattach HEAD ## may be needed for accurate code coverage info
# run: git checkout ${{ github.head_ref }} # run: git checkout ${{ github.head_ref }}
- name: Initialize workflow variables - name: Initialize workflow variables
@ -615,11 +767,6 @@ jobs:
# staging directory # staging directory
STAGING='_staging' STAGING='_staging'
outputs STAGING outputs STAGING
## # check for CODECOV_TOKEN availability (work-around for inaccessible 'secrets' object for 'if'; see <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
## # note: CODECOV_TOKEN / HAS_CODECOV_TOKEN is not needed for public repositories when using AppVeyor, Azure Pipelines, CircleCI, GitHub Actions, Travis (see <https://docs.codecov.io/docs/about-the-codecov-bash-uploader#section-upload-token>)
## unset HAS_CODECOV_TOKEN
## if [ -n $CODECOV_TOKEN ]; then HAS_CODECOV_TOKEN='true' ; fi
## outputs HAS_CODECOV_TOKEN
# target-specific options # target-specific options
# * CARGO_FEATURES_OPTION # * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='--all-features' ; ## default to '--all-features' for code coverage CARGO_FEATURES_OPTION='--all-features' ; ## default to '--all-features' for code coverage
@ -628,6 +775,13 @@ jobs:
# * CODECOV_FLAGS # * CODECOV_FLAGS
CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' ) CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' )
outputs CODECOV_FLAGS outputs CODECOV_FLAGS
- name: Install/setup prerequisites
shell: bash
run: |
## Install/setup prerequisites
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
esac
- name: rust toolchain ~ install - name: rust toolchain ~ install
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
@ -650,10 +804,10 @@ jobs:
command: test command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast -p uucore args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast -p uucore
env: env:
CARGO_INCREMENTAL: '0' CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: '' RUSTC_WRAPPER: ""
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort' RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
RUSTDOCFLAGS: '-Cpanic=abort' RUSTDOCFLAGS: "-Cpanic=abort"
# RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }} # RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }}
- name: Test - name: Test
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -661,10 +815,10 @@ jobs:
command: test command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
env: env:
CARGO_INCREMENTAL: '0' CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: '' RUSTC_WRAPPER: ""
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort' RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
RUSTDOCFLAGS: '-Cpanic=abort' RUSTDOCFLAGS: "-Cpanic=abort"
# RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }} # RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }}
- name: Test individual utilities - name: Test individual utilities
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -672,10 +826,10 @@ jobs:
command: test command: test
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
env: env:
CARGO_INCREMENTAL: '0' CARGO_INCREMENTAL: "0"
RUSTC_WRAPPER: '' RUSTC_WRAPPER: ""
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort' RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
RUSTDOCFLAGS: '-Cpanic=abort' RUSTDOCFLAGS: "-Cpanic=abort"
# RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }} # RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }}
- name: "`grcov` ~ install" - name: "`grcov` ~ install"
uses: actions-rs/install@v0.1 uses: actions-rs/install@v0.1
@ -708,35 +862,3 @@ jobs:
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
name: codecov-umbrella name: codecov-umbrella
fail_ci_if_error: false fail_ci_if_error: false
unused_deps:
name: Unused deps
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
- { os: macos-latest , features: feat_os_macos }
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v2
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
default: true
profile: minimal
- name: Install `cargo-udeps`
uses: actions-rs/install@v0.1
with:
crate: cargo-udeps
version: latest
use-tool-cache: true
env:
RUSTUP_TOOLCHAIN: stable
- name: Confirms there isn't any unused deps
shell: bash
run: |
cargo +nightly udeps --all-targets &> udeps.log || cat udeps.log
grep "seem to have been used" udeps.log

2
.vscode/cSpell.json vendored
View file

@ -11,7 +11,7 @@
{ "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" } { "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" }
], ],
// ignorePaths - a list of globs to specify which files are to be ignored // ignorePaths - a list of globs to specify which files are to be ignored
"ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**", "src/uu/dd/test-resources/**"], "ignorePaths": ["Cargo.lock", "target/**", "tests/**/fixtures/**", "src/uu/dd/test-resources/**", "vendor/**"],
// ignoreWords - a list of words to be ignored (even if they are in the flagWords) // ignoreWords - a list of words to be ignored (even if they are in the flagWords)
"ignoreWords": [], "ignoreWords": [],
// words - list of words to be always considered correct // words - list of words to be always considered correct

513
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
[package] [package]
name = "coreutils" name = "coreutils"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust" description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust"
@ -148,7 +148,7 @@ feat_os_unix_musl = [
# NOTE: # NOTE:
# The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time. # The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time.
# Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time. # Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time.
feat_selinux = ["cp/selinux", "id/selinux", "selinux", "feat_require_selinux"] feat_selinux = ["cp/selinux", "id/selinux", "ls/selinux", "selinux", "feat_require_selinux"]
# "feat_acl" == set of utilities providing support for acl (access control lists) if enabled with `--features feat_acl`. # "feat_acl" == set of utilities providing support for acl (access control lists) if enabled with `--features feat_acl`.
# NOTE: # NOTE:
# On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time. # On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time.
@ -247,110 +247,110 @@ test = [ "uu_test" ]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
lazy_static = { version="1.3" } lazy_static = { version="1.3" }
textwrap = { version="0.14", features=["terminal_size"] } textwrap = { version="0.14", features=["terminal_size"] }
uucore = { version=">=0.0.9", package="uucore", path="src/uucore" } uucore = { version=">=0.0.10", package="uucore", path="src/uucore" }
selinux = { version="0.2.3", optional = true } selinux = { version="0.2.3", optional = true }
# * uutils # * uutils
uu_test = { optional=true, version="0.0.7", package="uu_test", path="src/uu/test" } uu_test = { optional=true, version="0.0.8", package="uu_test", path="src/uu/test" }
# #
arch = { optional=true, version="0.0.7", package="uu_arch", path="src/uu/arch" } arch = { optional=true, version="0.0.8", package="uu_arch", path="src/uu/arch" }
base32 = { optional=true, version="0.0.7", package="uu_base32", path="src/uu/base32" } base32 = { optional=true, version="0.0.8", package="uu_base32", path="src/uu/base32" }
base64 = { optional=true, version="0.0.7", package="uu_base64", path="src/uu/base64" } base64 = { optional=true, version="0.0.8", package="uu_base64", path="src/uu/base64" }
basename = { optional=true, version="0.0.7", package="uu_basename", path="src/uu/basename" } basename = { optional=true, version="0.0.8", package="uu_basename", path="src/uu/basename" }
basenc = { optional=true, version="0.0.7", package="uu_basenc", path="src/uu/basenc" } basenc = { optional=true, version="0.0.8", package="uu_basenc", path="src/uu/basenc" }
cat = { optional=true, version="0.0.7", package="uu_cat", path="src/uu/cat" } cat = { optional=true, version="0.0.8", package="uu_cat", path="src/uu/cat" }
chcon = { optional=true, version="0.0.7", package="uu_chcon", path="src/uu/chcon" } chcon = { optional=true, version="0.0.8", package="uu_chcon", path="src/uu/chcon" }
chgrp = { optional=true, version="0.0.7", package="uu_chgrp", path="src/uu/chgrp" } chgrp = { optional=true, version="0.0.8", package="uu_chgrp", path="src/uu/chgrp" }
chmod = { optional=true, version="0.0.7", package="uu_chmod", path="src/uu/chmod" } chmod = { optional=true, version="0.0.8", package="uu_chmod", path="src/uu/chmod" }
chown = { optional=true, version="0.0.7", package="uu_chown", path="src/uu/chown" } chown = { optional=true, version="0.0.8", package="uu_chown", path="src/uu/chown" }
chroot = { optional=true, version="0.0.7", package="uu_chroot", path="src/uu/chroot" } chroot = { optional=true, version="0.0.8", package="uu_chroot", path="src/uu/chroot" }
cksum = { optional=true, version="0.0.7", package="uu_cksum", path="src/uu/cksum" } cksum = { optional=true, version="0.0.8", package="uu_cksum", path="src/uu/cksum" }
comm = { optional=true, version="0.0.7", package="uu_comm", path="src/uu/comm" } comm = { optional=true, version="0.0.8", package="uu_comm", path="src/uu/comm" }
cp = { optional=true, version="0.0.7", package="uu_cp", path="src/uu/cp" } cp = { optional=true, version="0.0.8", package="uu_cp", path="src/uu/cp" }
csplit = { optional=true, version="0.0.7", package="uu_csplit", path="src/uu/csplit" } csplit = { optional=true, version="0.0.8", package="uu_csplit", path="src/uu/csplit" }
cut = { optional=true, version="0.0.7", package="uu_cut", path="src/uu/cut" } cut = { optional=true, version="0.0.8", package="uu_cut", path="src/uu/cut" }
date = { optional=true, version="0.0.7", package="uu_date", path="src/uu/date" } date = { optional=true, version="0.0.8", package="uu_date", path="src/uu/date" }
dd = { optional=true, version="0.0.7", package="uu_dd", path="src/uu/dd" } dd = { optional=true, version="0.0.8", package="uu_dd", path="src/uu/dd" }
df = { optional=true, version="0.0.7", package="uu_df", path="src/uu/df" } df = { optional=true, version="0.0.8", package="uu_df", path="src/uu/df" }
dircolors= { optional=true, version="0.0.7", package="uu_dircolors", path="src/uu/dircolors" } dircolors= { optional=true, version="0.0.8", package="uu_dircolors", path="src/uu/dircolors" }
dirname = { optional=true, version="0.0.7", package="uu_dirname", path="src/uu/dirname" } dirname = { optional=true, version="0.0.8", package="uu_dirname", path="src/uu/dirname" }
du = { optional=true, version="0.0.7", package="uu_du", path="src/uu/du" } du = { optional=true, version="0.0.8", package="uu_du", path="src/uu/du" }
echo = { optional=true, version="0.0.7", package="uu_echo", path="src/uu/echo" } echo = { optional=true, version="0.0.8", package="uu_echo", path="src/uu/echo" }
env = { optional=true, version="0.0.7", package="uu_env", path="src/uu/env" } env = { optional=true, version="0.0.8", package="uu_env", path="src/uu/env" }
expand = { optional=true, version="0.0.7", package="uu_expand", path="src/uu/expand" } expand = { optional=true, version="0.0.8", package="uu_expand", path="src/uu/expand" }
expr = { optional=true, version="0.0.7", package="uu_expr", path="src/uu/expr" } expr = { optional=true, version="0.0.8", package="uu_expr", path="src/uu/expr" }
factor = { optional=true, version="0.0.7", package="uu_factor", path="src/uu/factor" } factor = { optional=true, version="0.0.8", package="uu_factor", path="src/uu/factor" }
false = { optional=true, version="0.0.7", package="uu_false", path="src/uu/false" } false = { optional=true, version="0.0.8", package="uu_false", path="src/uu/false" }
fmt = { optional=true, version="0.0.7", package="uu_fmt", path="src/uu/fmt" } fmt = { optional=true, version="0.0.8", package="uu_fmt", path="src/uu/fmt" }
fold = { optional=true, version="0.0.7", package="uu_fold", path="src/uu/fold" } fold = { optional=true, version="0.0.8", package="uu_fold", path="src/uu/fold" }
groups = { optional=true, version="0.0.7", package="uu_groups", path="src/uu/groups" } groups = { optional=true, version="0.0.8", package="uu_groups", path="src/uu/groups" }
hashsum = { optional=true, version="0.0.7", package="uu_hashsum", path="src/uu/hashsum" } hashsum = { optional=true, version="0.0.8", package="uu_hashsum", path="src/uu/hashsum" }
head = { optional=true, version="0.0.7", package="uu_head", path="src/uu/head" } head = { optional=true, version="0.0.8", package="uu_head", path="src/uu/head" }
hostid = { optional=true, version="0.0.7", package="uu_hostid", path="src/uu/hostid" } hostid = { optional=true, version="0.0.8", package="uu_hostid", path="src/uu/hostid" }
hostname = { optional=true, version="0.0.7", package="uu_hostname", path="src/uu/hostname" } hostname = { optional=true, version="0.0.8", package="uu_hostname", path="src/uu/hostname" }
id = { optional=true, version="0.0.7", package="uu_id", path="src/uu/id" } id = { optional=true, version="0.0.8", package="uu_id", path="src/uu/id" }
install = { optional=true, version="0.0.7", package="uu_install", path="src/uu/install" } install = { optional=true, version="0.0.8", package="uu_install", path="src/uu/install" }
join = { optional=true, version="0.0.7", package="uu_join", path="src/uu/join" } join = { optional=true, version="0.0.8", package="uu_join", path="src/uu/join" }
kill = { optional=true, version="0.0.7", package="uu_kill", path="src/uu/kill" } kill = { optional=true, version="0.0.8", package="uu_kill", path="src/uu/kill" }
link = { optional=true, version="0.0.7", package="uu_link", path="src/uu/link" } link = { optional=true, version="0.0.8", package="uu_link", path="src/uu/link" }
ln = { optional=true, version="0.0.7", package="uu_ln", path="src/uu/ln" } ln = { optional=true, version="0.0.8", package="uu_ln", path="src/uu/ln" }
ls = { optional=true, version="0.0.7", package="uu_ls", path="src/uu/ls" } ls = { optional=true, version="0.0.8", package="uu_ls", path="src/uu/ls" }
logname = { optional=true, version="0.0.7", package="uu_logname", path="src/uu/logname" } logname = { optional=true, version="0.0.8", package="uu_logname", path="src/uu/logname" }
mkdir = { optional=true, version="0.0.7", package="uu_mkdir", path="src/uu/mkdir" } mkdir = { optional=true, version="0.0.8", package="uu_mkdir", path="src/uu/mkdir" }
mkfifo = { optional=true, version="0.0.7", package="uu_mkfifo", path="src/uu/mkfifo" } mkfifo = { optional=true, version="0.0.8", package="uu_mkfifo", path="src/uu/mkfifo" }
mknod = { optional=true, version="0.0.7", package="uu_mknod", path="src/uu/mknod" } mknod = { optional=true, version="0.0.8", package="uu_mknod", path="src/uu/mknod" }
mktemp = { optional=true, version="0.0.7", package="uu_mktemp", path="src/uu/mktemp" } mktemp = { optional=true, version="0.0.8", package="uu_mktemp", path="src/uu/mktemp" }
more = { optional=true, version="0.0.7", package="uu_more", path="src/uu/more" } more = { optional=true, version="0.0.8", package="uu_more", path="src/uu/more" }
mv = { optional=true, version="0.0.7", package="uu_mv", path="src/uu/mv" } mv = { optional=true, version="0.0.8", package="uu_mv", path="src/uu/mv" }
nice = { optional=true, version="0.0.7", package="uu_nice", path="src/uu/nice" } nice = { optional=true, version="0.0.8", package="uu_nice", path="src/uu/nice" }
nl = { optional=true, version="0.0.7", package="uu_nl", path="src/uu/nl" } nl = { optional=true, version="0.0.8", package="uu_nl", path="src/uu/nl" }
nohup = { optional=true, version="0.0.7", package="uu_nohup", path="src/uu/nohup" } nohup = { optional=true, version="0.0.8", package="uu_nohup", path="src/uu/nohup" }
nproc = { optional=true, version="0.0.7", package="uu_nproc", path="src/uu/nproc" } nproc = { optional=true, version="0.0.8", package="uu_nproc", path="src/uu/nproc" }
numfmt = { optional=true, version="0.0.7", package="uu_numfmt", path="src/uu/numfmt" } numfmt = { optional=true, version="0.0.8", package="uu_numfmt", path="src/uu/numfmt" }
od = { optional=true, version="0.0.7", package="uu_od", path="src/uu/od" } od = { optional=true, version="0.0.8", package="uu_od", path="src/uu/od" }
paste = { optional=true, version="0.0.7", package="uu_paste", path="src/uu/paste" } paste = { optional=true, version="0.0.8", package="uu_paste", path="src/uu/paste" }
pathchk = { optional=true, version="0.0.7", package="uu_pathchk", path="src/uu/pathchk" } pathchk = { optional=true, version="0.0.8", package="uu_pathchk", path="src/uu/pathchk" }
pinky = { optional=true, version="0.0.7", package="uu_pinky", path="src/uu/pinky" } pinky = { optional=true, version="0.0.8", package="uu_pinky", path="src/uu/pinky" }
pr = { optional=true, version="0.0.7", package="uu_pr", path="src/uu/pr" } pr = { optional=true, version="0.0.8", package="uu_pr", path="src/uu/pr" }
printenv = { optional=true, version="0.0.7", package="uu_printenv", path="src/uu/printenv" } printenv = { optional=true, version="0.0.8", package="uu_printenv", path="src/uu/printenv" }
printf = { optional=true, version="0.0.7", package="uu_printf", path="src/uu/printf" } printf = { optional=true, version="0.0.8", package="uu_printf", path="src/uu/printf" }
ptx = { optional=true, version="0.0.7", package="uu_ptx", path="src/uu/ptx" } ptx = { optional=true, version="0.0.8", package="uu_ptx", path="src/uu/ptx" }
pwd = { optional=true, version="0.0.7", package="uu_pwd", path="src/uu/pwd" } pwd = { optional=true, version="0.0.8", package="uu_pwd", path="src/uu/pwd" }
readlink = { optional=true, version="0.0.7", package="uu_readlink", path="src/uu/readlink" } readlink = { optional=true, version="0.0.8", package="uu_readlink", path="src/uu/readlink" }
realpath = { optional=true, version="0.0.7", package="uu_realpath", path="src/uu/realpath" } realpath = { optional=true, version="0.0.8", package="uu_realpath", path="src/uu/realpath" }
relpath = { optional=true, version="0.0.7", package="uu_relpath", path="src/uu/relpath" } relpath = { optional=true, version="0.0.8", package="uu_relpath", path="src/uu/relpath" }
rm = { optional=true, version="0.0.7", package="uu_rm", path="src/uu/rm" } rm = { optional=true, version="0.0.8", package="uu_rm", path="src/uu/rm" }
rmdir = { optional=true, version="0.0.7", package="uu_rmdir", path="src/uu/rmdir" } rmdir = { optional=true, version="0.0.8", package="uu_rmdir", path="src/uu/rmdir" }
runcon = { optional=true, version="0.0.7", package="uu_runcon", path="src/uu/runcon" } runcon = { optional=true, version="0.0.8", package="uu_runcon", path="src/uu/runcon" }
seq = { optional=true, version="0.0.7", package="uu_seq", path="src/uu/seq" } seq = { optional=true, version="0.0.8", package="uu_seq", path="src/uu/seq" }
shred = { optional=true, version="0.0.7", package="uu_shred", path="src/uu/shred" } shred = { optional=true, version="0.0.8", package="uu_shred", path="src/uu/shred" }
shuf = { optional=true, version="0.0.7", package="uu_shuf", path="src/uu/shuf" } shuf = { optional=true, version="0.0.8", package="uu_shuf", path="src/uu/shuf" }
sleep = { optional=true, version="0.0.7", package="uu_sleep", path="src/uu/sleep" } sleep = { optional=true, version="0.0.8", package="uu_sleep", path="src/uu/sleep" }
sort = { optional=true, version="0.0.7", package="uu_sort", path="src/uu/sort" } sort = { optional=true, version="0.0.8", package="uu_sort", path="src/uu/sort" }
split = { optional=true, version="0.0.7", package="uu_split", path="src/uu/split" } split = { optional=true, version="0.0.8", package="uu_split", path="src/uu/split" }
stat = { optional=true, version="0.0.7", package="uu_stat", path="src/uu/stat" } stat = { optional=true, version="0.0.8", package="uu_stat", path="src/uu/stat" }
stdbuf = { optional=true, version="0.0.7", package="uu_stdbuf", path="src/uu/stdbuf" } stdbuf = { optional=true, version="0.0.8", package="uu_stdbuf", path="src/uu/stdbuf" }
sum = { optional=true, version="0.0.7", package="uu_sum", path="src/uu/sum" } sum = { optional=true, version="0.0.8", package="uu_sum", path="src/uu/sum" }
sync = { optional=true, version="0.0.7", package="uu_sync", path="src/uu/sync" } sync = { optional=true, version="0.0.8", package="uu_sync", path="src/uu/sync" }
tac = { optional=true, version="0.0.7", package="uu_tac", path="src/uu/tac" } tac = { optional=true, version="0.0.8", package="uu_tac", path="src/uu/tac" }
tail = { optional=true, version="0.0.7", package="uu_tail", path="src/uu/tail" } tail = { optional=true, version="0.0.8", package="uu_tail", path="src/uu/tail" }
tee = { optional=true, version="0.0.7", package="uu_tee", path="src/uu/tee" } tee = { optional=true, version="0.0.8", package="uu_tee", path="src/uu/tee" }
timeout = { optional=true, version="0.0.7", package="uu_timeout", path="src/uu/timeout" } timeout = { optional=true, version="0.0.8", package="uu_timeout", path="src/uu/timeout" }
touch = { optional=true, version="0.0.7", package="uu_touch", path="src/uu/touch" } touch = { optional=true, version="0.0.8", package="uu_touch", path="src/uu/touch" }
tr = { optional=true, version="0.0.7", package="uu_tr", path="src/uu/tr" } tr = { optional=true, version="0.0.8", package="uu_tr", path="src/uu/tr" }
true = { optional=true, version="0.0.7", package="uu_true", path="src/uu/true" } true = { optional=true, version="0.0.8", package="uu_true", path="src/uu/true" }
truncate = { optional=true, version="0.0.7", package="uu_truncate", path="src/uu/truncate" } truncate = { optional=true, version="0.0.8", package="uu_truncate", path="src/uu/truncate" }
tsort = { optional=true, version="0.0.7", package="uu_tsort", path="src/uu/tsort" } tsort = { optional=true, version="0.0.8", package="uu_tsort", path="src/uu/tsort" }
tty = { optional=true, version="0.0.7", package="uu_tty", path="src/uu/tty" } tty = { optional=true, version="0.0.8", package="uu_tty", path="src/uu/tty" }
uname = { optional=true, version="0.0.7", package="uu_uname", path="src/uu/uname" } uname = { optional=true, version="0.0.8", package="uu_uname", path="src/uu/uname" }
unexpand = { optional=true, version="0.0.7", package="uu_unexpand", path="src/uu/unexpand" } unexpand = { optional=true, version="0.0.8", package="uu_unexpand", path="src/uu/unexpand" }
uniq = { optional=true, version="0.0.7", package="uu_uniq", path="src/uu/uniq" } uniq = { optional=true, version="0.0.8", package="uu_uniq", path="src/uu/uniq" }
unlink = { optional=true, version="0.0.7", package="uu_unlink", path="src/uu/unlink" } unlink = { optional=true, version="0.0.8", package="uu_unlink", path="src/uu/unlink" }
uptime = { optional=true, version="0.0.7", package="uu_uptime", path="src/uu/uptime" } uptime = { optional=true, version="0.0.8", package="uu_uptime", path="src/uu/uptime" }
users = { optional=true, version="0.0.7", package="uu_users", path="src/uu/users" } users = { optional=true, version="0.0.8", package="uu_users", path="src/uu/users" }
wc = { optional=true, version="0.0.7", package="uu_wc", path="src/uu/wc" } wc = { optional=true, version="0.0.8", package="uu_wc", path="src/uu/wc" }
who = { optional=true, version="0.0.7", package="uu_who", path="src/uu/who" } who = { optional=true, version="0.0.8", package="uu_who", path="src/uu/who" }
whoami = { optional=true, version="0.0.7", package="uu_whoami", path="src/uu/whoami" } whoami = { optional=true, version="0.0.8", package="uu_whoami", path="src/uu/whoami" }
yes = { optional=true, version="0.0.7", package="uu_yes", path="src/uu/yes" } yes = { optional=true, version="0.0.8", package="uu_yes", path="src/uu/yes" }
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)" # this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" } # factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
@ -373,7 +373,7 @@ sha1 = { version="0.6", features=["std"] }
tempfile = "3.2.0" tempfile = "3.2.0"
time = "0.1" time = "0.1"
unindent = "0.1" unindent = "0.1"
uucore = { version=">=0.0.9", package="uucore", path="src/uucore", features=["entries", "process"] } uucore = { version=">=0.0.10", package="uucore", path="src/uucore", features=["entries", "process"] }
walkdir = "2.2" walkdir = "2.2"
atty = "0.2" atty = "0.2"
@ -381,12 +381,15 @@ atty = "0.2"
rlimit = "0.4.0" rlimit = "0.4.0"
[target.'cfg(unix)'.dev-dependencies] [target.'cfg(unix)'.dev-dependencies]
nix = "0.20.0" nix = "=0.23.1"
rust-users = { version="0.10", package="users" } rust-users = { version="0.10", package="users" }
unix_socket = "0.5.0" unix_socket = "0.5.0"
[[bin]] [[bin]]
name = "coreutils" name = "coreutils"
path = "src/bin/coreutils.rs" path = "src/bin/coreutils.rs"
[patch.crates-io]
# FixME: [2021-11-16; rivy] remove 'nix' patch when MacOS compatibility is restored; ref: <https://github.com/nix-rust/nix/pull/1590>
# nix = { git = "https://github.com/rivy-t/nix" }
nix = { path = "vendor/nix-v0.23.1-patched" }

View file

@ -46,6 +46,8 @@ pub fn main() {
"type UtilityMap<T> = HashMap<&'static str, (fn(T) -> i32, fn() -> App<'static, 'static>)>;\n\ "type UtilityMap<T> = HashMap<&'static str, (fn(T) -> i32, fn() -> App<'static, 'static>)>;\n\
\n\ \n\
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n\ fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n\
\t#[allow(unused_mut)]\n\
\t#[allow(clippy::let_and_return)]\n\
\tlet mut map = UtilityMap::new();\n\ \tlet mut map = UtilityMap::new();\n\
" "
.as_bytes(), .as_bytes(),

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_arch" name = "uu_arch"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "arch ~ (uutils) display machine architecture" description = "arch ~ (uutils) display machine architecture"
@ -17,9 +17,12 @@ path = "src/arch.rs"
[dependencies] [dependencies]
platform-info = "0.1" platform-info = "0.1"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "arch" name = "arch"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -6,9 +6,6 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
#[macro_use]
extern crate uucore;
use platform_info::*; use platform_info::*;
use clap::{crate_version, App}; use clap::{crate_version, App};

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_base32" name = "uu_base32"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "base32 ~ (uutils) decode/encode input (base32-encoding)" description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
@ -16,13 +16,12 @@ path = "src/base32.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features = ["encoding"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "base32" name = "base32"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -5,13 +5,10 @@
// For the full copyright and license information, please view the LICENSE file // For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code. // that was distributed with this source code.
#[macro_use]
extern crate uucore;
use std::io::{stdin, Read}; use std::io::{stdin, Read};
use clap::App; use clap::App;
use uucore::encoding::Format; use uucore::{encoding::Format, error::UResult};
pub mod base_common; pub mod base_common;
@ -24,27 +21,22 @@ static ABOUT: &str = "
to attempt to recover from any other non-alphabet bytes in the to attempt to recover from any other non-alphabet bytes in the
encoded stream. encoded stream.
"; ";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static BASE_CMD_PARSE_ERROR: i32 = 1;
fn usage() -> String { fn usage() -> String {
format!("{0} [OPTION]... [FILE]", uucore::execution_phrase()) format!("{0} [OPTION]... [FILE]", uucore::execution_phrase())
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let format = Format::Base32; let format = Format::Base32;
let usage = usage(); let usage = usage();
let name = uucore::util_name();
let config_result: Result<base_common::Config, String> = let config: base_common::Config = base_common::parse_base_cmd_args(args, ABOUT, &usage)?;
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
// Create a reference to stdin so we can return a locked stdin from // Create a reference to stdin so we can return a locked stdin from
// parse_base_cmd_args // parse_base_cmd_args
let stdin_raw = stdin(); let stdin_raw = stdin();
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw); let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw)?;
base_common::handle_input( base_common::handle_input(
&mut input, &mut input,
@ -52,12 +44,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
config.wrap_cols, config.wrap_cols,
config.ignore_garbage, config.ignore_garbage,
config.decode, config.decode,
name, )
);
0
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
base_common::base_app(uucore::util_name(), VERSION, ABOUT) base_common::base_app(ABOUT)
} }

View file

@ -11,13 +11,16 @@ use std::io::{stdout, Read, Write};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::encoding::{wrap_print, Data, Format}; use uucore::encoding::{wrap_print, Data, Format};
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
use std::fs::File; use std::fs::File;
use std::io::{BufReader, Stdin}; use std::io::{BufReader, Stdin};
use std::path::Path; use std::path::Path;
use clap::{App, Arg}; use clap::{crate_version, App, Arg};
pub static BASE_CMD_PARSE_ERROR: i32 = 1;
// Config. // Config.
pub struct Config { pub struct Config {
@ -35,15 +38,14 @@ pub mod options {
} }
impl Config { impl Config {
pub fn from(app_name: &str, options: &clap::ArgMatches) -> Result<Config, String> { pub fn from(options: &clap::ArgMatches) -> UResult<Config> {
let file: Option<String> = match options.values_of(options::FILE) { let file: Option<String> = match options.values_of(options::FILE) {
Some(mut values) => { Some(mut values) => {
let name = values.next().unwrap(); let name = values.next().unwrap();
if let Some(extra_op) = values.next() { if let Some(extra_op) = values.next() {
return Err(format!( return Err(UUsageError::new(
"extra operand {}\nTry '{} --help' for more information.", BASE_CMD_PARSE_ERROR,
extra_op.quote(), format!("extra operand {}", extra_op.quote(),),
app_name
)); ));
} }
@ -51,7 +53,10 @@ impl Config {
None None
} else { } else {
if !Path::exists(Path::new(name)) { if !Path::exists(Path::new(name)) {
return Err(format!("{}: No such file or directory", name.maybe_quote())); return Err(USimpleError::new(
BASE_CMD_PARSE_ERROR,
format!("{}: No such file or directory", name.maybe_quote()),
));
} }
Some(name.to_owned()) Some(name.to_owned())
} }
@ -62,8 +67,12 @@ impl Config {
let cols = options let cols = options
.value_of(options::WRAP) .value_of(options::WRAP)
.map(|num| { .map(|num| {
num.parse::<usize>() num.parse::<usize>().map_err(|_| {
.map_err(|_| format!("invalid wrap size: {}", num.quote())) USimpleError::new(
BASE_CMD_PARSE_ERROR,
format!("invalid wrap size: {}", num.quote()),
)
})
}) })
.transpose()?; .transpose()?;
@ -76,23 +85,17 @@ impl Config {
} }
} }
pub fn parse_base_cmd_args( pub fn parse_base_cmd_args(args: impl uucore::Args, about: &str, usage: &str) -> UResult<Config> {
args: impl uucore::Args, let app = base_app(about).usage(usage);
name: &str,
version: &str,
about: &str,
usage: &str,
) -> Result<Config, String> {
let app = base_app(name, version, about).usage(usage);
let arg_list = args let arg_list = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
Config::from(name, &app.get_matches_from(arg_list)) Config::from(&app.get_matches_from(arg_list))
} }
pub fn base_app<'a>(name: &str, version: &'a str, about: &'a str) -> App<'static, 'a> { pub fn base_app<'a>(about: &'a str) -> App<'static, 'a> {
App::new(name) App::new(uucore::util_name())
.version(version) .version(crate_version!())
.about(about) .about(about)
// Format arguments. // Format arguments.
.arg( .arg(
@ -121,14 +124,15 @@ pub fn base_app<'a>(name: &str, version: &'a str, about: &'a str) -> App<'static
.arg(Arg::with_name(options::FILE).index(1).multiple(true)) .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> { pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> UResult<Box<dyn Read + 'a>> {
match &config.to_read { match &config.to_read {
Some(name) => { Some(name) => {
let file_buf = crash_if_err!(1, File::open(Path::new(name))); let file_buf =
Box::new(BufReader::new(file_buf)) // as Box<dyn Read> File::open(Path::new(name)).map_err_context(|| name.maybe_quote().to_string())?;
Ok(Box::new(BufReader::new(file_buf))) // as Box<dyn Read>
} }
None => { None => {
Box::new(stdin_ref.lock()) // as Box<dyn Read> Ok(Box::new(stdin_ref.lock())) // as Box<dyn Read>
} }
} }
} }
@ -139,8 +143,7 @@ pub fn handle_input<R: Read>(
line_wrap: Option<usize>, line_wrap: Option<usize>,
ignore_garbage: bool, ignore_garbage: bool,
decode: bool, decode: bool,
name: &str, ) -> UResult<()> {
) {
let mut data = Data::new(input, format).ignore_garbage(ignore_garbage); let mut data = Data::new(input, format).ignore_garbage(ignore_garbage);
if let Some(wrap) = line_wrap { if let Some(wrap) = line_wrap {
data = data.line_wrap(wrap); data = data.line_wrap(wrap);
@ -150,28 +153,25 @@ pub fn handle_input<R: Read>(
match data.encode() { match data.encode() {
Ok(s) => { Ok(s) => {
wrap_print(&data, s); wrap_print(&data, s);
Ok(())
} }
Err(_) => { Err(_) => Err(USimpleError::new(
eprintln!( 1,
"{}: error: invalid input (length must be multiple of 4 characters)", "error: invalid input (length must be multiple of 4 characters)",
name )),
);
exit!(1)
}
} }
} else { } else {
match data.decode() { match data.decode() {
Ok(s) => { Ok(s) => {
// Silent the warning as we want to the error message
#[allow(clippy::question_mark)]
if stdout().write_all(&s).is_err() { if stdout().write_all(&s).is_err() {
// on windows console, writing invalid utf8 returns an error // on windows console, writing invalid utf8 returns an error
eprintln!("{}: error: Cannot write non-utf8 data", name); return Err(USimpleError::new(1, "error: cannot write non-utf8 data"));
exit!(1)
} }
Ok(())
} }
Err(_) => { Err(_) => Err(USimpleError::new(1, "error: invalid input")),
eprintln!("{}: error: invalid input", name);
exit!(1)
}
} }
} }
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_base64" name = "uu_base64"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "base64 ~ (uutils) decode/encode input (base64-encoding)" description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
@ -16,14 +16,13 @@ path = "src/base64.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features = ["encoding"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"} uu_base32 = { version=">=0.0.8", package="uu_base32", path="../base32"}
[[bin]] [[bin]]
name = "base64" name = "base64"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -6,13 +6,10 @@
// For the full copyright and license information, please view the LICENSE file // For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code. // that was distributed with this source code.
#[macro_use]
extern crate uucore;
use uu_base32::base_common; use uu_base32::base_common;
pub use uu_base32::uu_app; pub use uu_base32::uu_app;
use uucore::encoding::Format; use uucore::{encoding::Format, error::UResult};
use std::io::{stdin, Read}; use std::io::{stdin, Read};
@ -25,26 +22,22 @@ static ABOUT: &str = "
to attempt to recover from any other non-alphabet bytes in the to attempt to recover from any other non-alphabet bytes in the
encoded stream. encoded stream.
"; ";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static BASE_CMD_PARSE_ERROR: i32 = 1;
fn usage() -> String { fn usage() -> String {
format!("{0} [OPTION]... [FILE]", uucore::execution_phrase()) format!("{0} [OPTION]... [FILE]", uucore::execution_phrase())
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let format = Format::Base64; let format = Format::Base64;
let usage = usage(); let usage = usage();
let name = uucore::util_name();
let config_result: Result<base_common::Config, String> = let config: base_common::Config = base_common::parse_base_cmd_args(args, ABOUT, &usage)?;
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
// Create a reference to stdin so we can return a locked stdin from // Create a reference to stdin so we can return a locked stdin from
// parse_base_cmd_args // parse_base_cmd_args
let stdin_raw = stdin(); let stdin_raw = stdin();
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw); let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw)?;
base_common::handle_input( base_common::handle_input(
&mut input, &mut input,
@ -52,8 +45,5 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
config.wrap_cols, config.wrap_cols,
config.ignore_garbage, config.ignore_garbage,
config.decode, config.decode,
name, )
);
0
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_basename" name = "uu_basename"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "basename ~ (uutils) display PATHNAME with leading directory components removed" description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
@ -16,13 +16,12 @@ path = "src/basename.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "basename" name = "basename"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -131,21 +131,15 @@ fn basename(fullname: &str, suffix: &str) -> String {
// Convert to path buffer and get last path component // Convert to path buffer and get last path component
let pb = PathBuf::from(path); let pb = PathBuf::from(path);
match pb.components().last() { match pb.components().last() {
Some(c) => strip_suffix(c.as_os_str().to_str().unwrap(), suffix), Some(c) => {
let name = c.as_os_str().to_str().unwrap();
if name == suffix {
name.to_string()
} else {
name.strip_suffix(suffix).unwrap_or(name).to_string()
}
}
None => "".to_owned(), None => "".to_owned(),
} }
} }
// can be replaced with strip_suffix once MSRV is 1.45
#[allow(clippy::manual_strip)]
fn strip_suffix(name: &str, suffix: &str) -> String {
if name == suffix {
return name.to_owned();
}
if name.ends_with(suffix) {
return name[..name.len() - suffix.len()].to_owned();
}
name.to_owned()
}

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_basenc" name = "uu_basenc"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "basenc ~ (uutils) decode/encode input" description = "basenc ~ (uutils) decode/encode input"
@ -16,14 +16,13 @@ path = "src/basenc.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features = ["encoding"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"} uu_base32 = { version=">=0.0.8", package="uu_base32", path="../base32"}
[[bin]] [[bin]]
name = "basenc" name = "basenc"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -8,13 +8,14 @@
//spell-checker:ignore (args) lsbf msbf //spell-checker:ignore (args) lsbf msbf
#[macro_use] use clap::{App, Arg};
extern crate uucore; use uu_base32::base_common::{self, Config, BASE_CMD_PARSE_ERROR};
use clap::{crate_version, App, Arg}; use uucore::{
use uu_base32::base_common::{self, Config}; encoding::Format,
error::{UResult, UUsageError},
use uucore::{encoding::Format, InvalidEncodingHandling}; InvalidEncodingHandling,
};
use std::io::{stdin, Read}; use std::io::{stdin, Read};
@ -26,8 +27,6 @@ static ABOUT: &str = "
from any other non-alphabet bytes in the encoded stream. from any other non-alphabet bytes in the encoded stream.
"; ";
static BASE_CMD_PARSE_ERROR: i32 = 1;
const ENCODINGS: &[(&str, Format)] = &[ const ENCODINGS: &[(&str, Format)] = &[
("base64", Format::Base64), ("base64", Format::Base64),
("base64url", Format::Base64Url), ("base64url", Format::Base64Url),
@ -47,14 +46,14 @@ fn usage() -> String {
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
let mut app = base_common::base_app(uucore::util_name(), crate_version!(), ABOUT); let mut app = base_common::base_app(ABOUT);
for encoding in ENCODINGS { for encoding in ENCODINGS {
app = app.arg(Arg::with_name(encoding.0).long(encoding.0)); app = app.arg(Arg::with_name(encoding.0).long(encoding.0));
} }
app app
} }
fn parse_cmd_args(args: impl uucore::Args) -> (Config, Format) { fn parse_cmd_args(args: impl uucore::Args) -> UResult<(Config, Format)> {
let usage = usage(); let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from( let matches = uu_app().usage(&usage[..]).get_matches_from(
args.collect_str(InvalidEncodingHandling::ConvertLossy) args.collect_str(InvalidEncodingHandling::ConvertLossy)
@ -63,24 +62,19 @@ fn parse_cmd_args(args: impl uucore::Args) -> (Config, Format) {
let format = ENCODINGS let format = ENCODINGS
.iter() .iter()
.find(|encoding| matches.is_present(encoding.0)) .find(|encoding| matches.is_present(encoding.0))
.unwrap_or_else(|| { .ok_or_else(|| UUsageError::new(BASE_CMD_PARSE_ERROR, "missing encoding type"))?
show_usage_error!("missing encoding type");
std::process::exit(1)
})
.1; .1;
( let config = Config::from(&matches)?;
Config::from("basenc", &matches).unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s)), Ok((config, format))
format,
)
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
let name = uucore::util_name(); pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let (config, format) = parse_cmd_args(args); let (config, format) = parse_cmd_args(args)?;
// Create a reference to stdin so we can return a locked stdin from // Create a reference to stdin so we can return a locked stdin from
// parse_base_cmd_args // parse_base_cmd_args
let stdin_raw = stdin(); let stdin_raw = stdin();
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw); let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw)?;
base_common::handle_input( base_common::handle_input(
&mut input, &mut input,
@ -88,8 +82,5 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
config.wrap_cols, config.wrap_cols,
config.ignore_garbage, config.ignore_garbage,
config.decode, config.decode,
name, )
);
0
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_cat" name = "uu_cat"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "cat ~ (uutils) concatenate and display input" description = "cat ~ (uutils) concatenate and display input"
@ -18,12 +18,12 @@ path = "src/cat.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
thiserror = "1.0" thiserror = "1.0"
atty = "0.2" atty = "0.2"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "pipes"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "pipes"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
unix_socket = "0.5.0" unix_socket = "0.5.0"
nix = "0.20.0" nix = "=0.23.1"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi-util = "0.1.5" winapi-util = "0.1.5"
@ -31,3 +31,6 @@ winapi-util = "0.1.5"
[[bin]] [[bin]]
name = "cat" name = "cat"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -12,8 +12,6 @@
#[cfg(unix)] #[cfg(unix)]
extern crate unix_socket; extern crate unix_socket;
#[macro_use]
extern crate uucore;
// last synced with: cat (GNU coreutils) 8.13 // last synced with: cat (GNU coreutils) 8.13
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
@ -590,7 +588,7 @@ fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize {
fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) -> usize { fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) -> usize {
let mut count = 0; let mut count = 0;
for byte in in_buf.iter().map(|c| *c) { for byte in in_buf.iter().copied() {
if byte == b'\n' { if byte == b'\n' {
break; break;
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_chcon" name = "uu_chcon"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "chcon ~ (uutils) change file security context" description = "chcon ~ (uutils) change file security context"
@ -25,3 +25,6 @@ libc = { version = "0.2" }
[[bin]] [[bin]]
name = "chcon" name = "chcon"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -707,7 +707,7 @@ fn root_dev_ino_warn(dir_name: &Path) {
// When a program like chgrp performs a recursive traversal that requires traversing symbolic links, // When a program like chgrp performs a recursive traversal that requires traversing symbolic links,
// it is *not* a problem. // it is *not* a problem.
// However, when invoked with "-P -R", it deserves a warning. // However, when invoked with "-P -R", it deserves a warning.
// The fts_options parameter records the options that control this aspect of fts's behavior, // The fts_options parameter records the options that control this aspect of fts behavior,
// so test that. // so test that.
fn cycle_warning_required(fts_options: c_int, entry: &fts::EntryRef) -> bool { fn cycle_warning_required(fts_options: c_int, entry: &fts::EntryRef) -> bool {
// When dereferencing no symlinks, or when dereferencing only those listed on the command line // When dereferencing no symlinks, or when dereferencing only those listed on the command line

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_chgrp" name = "uu_chgrp"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "chgrp ~ (uutils) change the group ownership of FILE" description = "chgrp ~ (uutils) change the group ownership of FILE"
@ -16,9 +16,12 @@ path = "src/chgrp.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "chgrp" name = "chgrp"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -7,8 +7,6 @@
// spell-checker:ignore (ToDO) COMFOLLOW Chowner RFILE RFILE's derefer dgid nonblank nonprint nonprinting // spell-checker:ignore (ToDO) COMFOLLOW Chowner RFILE RFILE's derefer dgid nonblank nonprint nonprinting
#[macro_use]
extern crate uucore;
use uucore::display::Quotable; use uucore::display::Quotable;
pub use uucore::entries; pub use uucore::entries;
use uucore::error::{FromIo, UResult, USimpleError}; use uucore::error::{FromIo, UResult, USimpleError};

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_chmod" name = "uu_chmod"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "chmod ~ (uutils) change mode of FILE" description = "chmod ~ (uutils) change mode of FILE"
@ -17,10 +17,13 @@ path = "src/chmod.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "mode"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
walkdir = "2.2" walkdir = "2.2"
[[bin]] [[bin]]
name = "chmod" name = "chmod"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -7,19 +7,17 @@
// spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's // spell-checker:ignore (ToDO) Chmoder cmode fmode fperm fref ugoa RFILE RFILE's
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::fs; use std::fs;
use std::os::unix::fs::{MetadataExt, PermissionsExt}; use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{ExitCode, UResult, USimpleError, UUsageError};
use uucore::fs::display_permissions_unix; use uucore::fs::display_permissions_unix;
use uucore::libc::mode_t; use uucore::libc::mode_t;
#[cfg(not(windows))] #[cfg(not(windows))]
use uucore::mode; use uucore::mode;
use uucore::InvalidEncodingHandling; use uucore::{show_error, InvalidEncodingHandling};
use walkdir::WalkDir; use walkdir::WalkDir;
static ABOUT: &str = "Change the mode of each FILE to MODE. static ABOUT: &str = "Change the mode of each FILE to MODE.
@ -50,14 +48,15 @@ fn get_long_usage() -> String {
String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.") String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.")
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let mut args = args let mut args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
// Before we can parse 'args' with clap (and previously getopts), // Before we can parse 'args' with clap (and previously getopts),
// a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE"). // a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE").
let mode_had_minus_prefix = strip_minus_from_mode(&mut args); let mode_had_minus_prefix = mode::strip_minus_from_mode(&mut args);
let usage = usage(); let usage = usage();
let after_help = get_long_usage(); let after_help = get_long_usage();
@ -72,12 +71,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let verbose = matches.is_present(options::VERBOSE); let verbose = matches.is_present(options::VERBOSE);
let preserve_root = matches.is_present(options::PRESERVE_ROOT); let preserve_root = matches.is_present(options::PRESERVE_ROOT);
let recursive = matches.is_present(options::RECURSIVE); let recursive = matches.is_present(options::RECURSIVE);
let fmode = matches let fmode = match matches.value_of(options::REFERENCE) {
.value_of(options::REFERENCE) Some(fref) => match fs::metadata(fref) {
.and_then(|fref| match fs::metadata(fref) {
Ok(meta) => Some(meta.mode()), Ok(meta) => Some(meta.mode()),
Err(err) => crash!(1, "cannot stat attributes of {}: {}", fref.quote(), err), Err(err) => {
}); return Err(USimpleError::new(
1,
format!("cannot stat attributes of {}: {}", fref.quote(), err),
))
}
},
None => None,
};
let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
let cmode = if mode_had_minus_prefix { let cmode = if mode_had_minus_prefix {
// clap parsing is finished, now put prefix back // clap parsing is finished, now put prefix back
@ -100,7 +105,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}; };
if files.is_empty() { if files.is_empty() {
crash!(1, "missing operand"); return Err(UUsageError::new(1, "missing operand".to_string()));
} }
let chmoder = Chmoder { let chmoder = Chmoder {
@ -112,12 +117,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmode, fmode,
cmode, cmode,
}; };
match chmoder.chmod(files) {
Ok(()) => {}
Err(e) => return e,
}
0 chmoder.chmod(files)
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
@ -180,30 +181,6 @@ pub fn uu_app() -> App<'static, 'static> {
) )
} }
// Iterate 'args' and delete the first occurrence
// of a prefix '-' if it's associated with MODE
// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE"
pub fn strip_minus_from_mode(args: &mut Vec<String>) -> bool {
for arg in args {
if arg == "--" {
break;
}
if arg.starts_with('-') {
if let Some(second) = arg.chars().nth(1) {
match second {
'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => {
// TODO: use strip_prefix() once minimum rust version reaches 1.45.0
*arg = arg[1..arg.len()].to_string();
return true;
}
_ => {}
}
}
}
}
false
}
struct Chmoder { struct Chmoder {
changes: bool, changes: bool,
quiet: bool, quiet: bool,
@ -215,7 +192,7 @@ struct Chmoder {
} }
impl Chmoder { impl Chmoder {
fn chmod(&self, files: Vec<String>) -> Result<(), i32> { fn chmod(&self, files: Vec<String>) -> UResult<()> {
let mut r = Ok(()); let mut r = Ok(());
for filename in &files { for filename in &files {
@ -228,22 +205,30 @@ impl Chmoder {
filename.quote() filename.quote()
); );
if !self.quiet { if !self.quiet {
show_error!("cannot operate on dangling symlink {}", filename.quote()); return Err(USimpleError::new(
1,
format!("cannot operate on dangling symlink {}", filename.quote()),
));
} }
} else if !self.quiet { } else if !self.quiet {
show_error!( return Err(USimpleError::new(
1,
format!(
"cannot access {}: No such file or directory", "cannot access {}: No such file or directory",
filename.quote() filename.quote()
); ),
));
} }
return Err(1); return Err(ExitCode::new(1));
} }
if self.recursive && self.preserve_root && filename == "/" { if self.recursive && self.preserve_root && filename == "/" {
show_error!( return Err(USimpleError::new(
1,
format!(
"it is dangerous to operate recursively on {}\nuse --no-preserve-root to override this failsafe", "it is dangerous to operate recursively on {}\nuse --no-preserve-root to override this failsafe",
filename.quote() filename.quote()
); )
return Err(1); ));
} }
if !self.recursive { if !self.recursive {
r = self.chmod_file(file).and(r); r = self.chmod_file(file).and(r);
@ -258,14 +243,14 @@ impl Chmoder {
} }
#[cfg(windows)] #[cfg(windows)]
fn chmod_file(&self, file: &Path) -> Result<(), i32> { fn chmod_file(&self, file: &Path) -> UResult<()> {
// chmod is useless on Windows // chmod is useless on Windows
// it doesn't set any permissions at all // it doesn't set any permissions at all
// instead it just sets the readonly attribute on the file // instead it just sets the readonly attribute on the file
Err(0) Ok(())
} }
#[cfg(unix)] #[cfg(unix)]
fn chmod_file(&self, file: &Path) -> Result<(), i32> { fn chmod_file(&self, file: &Path) -> UResult<()> {
use uucore::mode::get_umask; use uucore::mode::get_umask;
let fperm = match fs::metadata(file) { let fperm = match fs::metadata(file) {
@ -282,11 +267,13 @@ impl Chmoder {
} else if err.kind() == std::io::ErrorKind::PermissionDenied { } else if err.kind() == std::io::ErrorKind::PermissionDenied {
// These two filenames would normally be conditionally // These two filenames would normally be conditionally
// quoted, but GNU's tests expect them to always be quoted // quoted, but GNU's tests expect them to always be quoted
show_error!("{}: Permission denied", file.quote()); return Err(USimpleError::new(
1,
format!("{}: Permission denied", file.quote()),
));
} else { } else {
show_error!("{}: {}", file.quote(), err); return Err(USimpleError::new(1, format!("{}: {}", file.quote(), err)));
} }
return Err(1);
} }
}; };
match self.fmode { match self.fmode {
@ -320,22 +307,25 @@ impl Chmoder {
} }
Err(f) => { Err(f) => {
if !self.quiet { if !self.quiet {
show_error!("{}", f); return Err(USimpleError::new(1, f));
} else {
return Err(ExitCode::new(1));
} }
return Err(1);
} }
} }
} }
self.change_file(fperm, new_mode, file)?; self.change_file(fperm, new_mode, file)?;
// if a permission would have been removed if umask was 0, but it wasn't because umask was not 0, print an error and fail // if a permission would have been removed if umask was 0, but it wasn't because umask was not 0, print an error and fail
if (new_mode & !naively_expected_new_mode) != 0 { if (new_mode & !naively_expected_new_mode) != 0 {
show_error!( return Err(USimpleError::new(
1,
format!(
"{}: new permissions are {}, not {}", "{}: new permissions are {}, not {}",
file.maybe_quote(), file.maybe_quote(),
display_permissions_unix(new_mode as mode_t, false), display_permissions_unix(new_mode as mode_t, false),
display_permissions_unix(naively_expected_new_mode as mode_t, false) display_permissions_unix(naively_expected_new_mode as mode_t, false)
); ),
return Err(1); ));
} }
} }
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_chown" name = "uu_chown"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "chown ~ (uutils) change the ownership of FILE" description = "chown ~ (uutils) change the ownership of FILE"
@ -16,9 +16,12 @@ path = "src/chown.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "chown" name = "chown"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -7,8 +7,6 @@
// spell-checker:ignore (ToDO) COMFOLLOW Passwd RFILE RFILE's derefer dgid duid groupname // spell-checker:ignore (ToDO) COMFOLLOW Passwd RFILE RFILE's derefer dgid duid groupname
#[macro_use]
extern crate uucore;
use uucore::display::Quotable; use uucore::display::Quotable;
pub use uucore::entries::{self, Group, Locate, Passwd}; pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::perms::{chown_base, options, IfFrom}; use uucore::perms::{chown_base, options, IfFrom};

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_chroot" name = "uu_chroot"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "chroot ~ (uutils) run COMMAND under a new root directory" description = "chroot ~ (uutils) run COMMAND under a new root directory"
@ -16,9 +16,12 @@ path = "src/chroot.rs"
[dependencies] [dependencies]
clap= "2.33" clap= "2.33"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "chroot" name = "chroot"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -67,7 +67,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
// TODO: refactor the args and command matching // TODO: refactor the args and command matching
// See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967 // See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967
let command: Vec<&str> = match commands.len() { let command: Vec<&str> = match commands.len() {
1 => { 0 => {
let shell: &str = match user_shell { let shell: &str = match user_shell {
Err(_) => default_shell, Err(_) => default_shell,
Ok(ref s) => s.as_ref(), Ok(ref s) => s.as_ref(),
@ -77,12 +77,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
_ => commands, _ => commands,
}; };
assert!(!command.is_empty());
let chroot_command = command[0];
let chroot_args = &command[1..];
// NOTE: Tests can only trigger code beyond this point if they're invoked with root permissions
set_context(newroot, &matches); set_context(newroot, &matches);
let pstatus = Command::new(command[0]) let pstatus = Command::new(chroot_command)
.args(&command[1..]) .args(chroot_args)
.status() .status()
.unwrap_or_else(|e| crash!(1, "Cannot exec: {}", e)); .unwrap_or_else(|e| {
// TODO: Exit status:
// 125 if chroot itself fails
// 126 if command is found but cannot be invoked
// 127 if command cannot be found
crash!(
1,
"failed to run command {}: {}",
command[0].to_string().quote(),
e
)
});
if pstatus.success() { if pstatus.success() {
0 0

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_cksum" name = "uu_cksum"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "cksum ~ (uutils) display CRC and size of input" description = "cksum ~ (uutils) display CRC and size of input"
@ -17,13 +17,12 @@ path = "src/cksum.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "cksum" name = "cksum"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -25,60 +25,15 @@ const NAME: &str = "cksum";
const SYNTAX: &str = "[OPTIONS] [FILE]..."; const SYNTAX: &str = "[OPTIONS] [FILE]...";
const SUMMARY: &str = "Print CRC and size for each file"; const SUMMARY: &str = "Print CRC and size for each file";
// this is basically a hack to get "loops" to work on Rust 1.33. Once we update to Rust 1.46 or
// greater, we can just use while loops
macro_rules! unroll {
(256, |$i:ident| $s:expr) => {{
unroll!(@ 32, 0 * 32, $i, $s);
unroll!(@ 32, 1 * 32, $i, $s);
unroll!(@ 32, 2 * 32, $i, $s);
unroll!(@ 32, 3 * 32, $i, $s);
unroll!(@ 32, 4 * 32, $i, $s);
unroll!(@ 32, 5 * 32, $i, $s);
unroll!(@ 32, 6 * 32, $i, $s);
unroll!(@ 32, 7 * 32, $i, $s);
}};
(8, |$i:ident| $s:expr) => {{
unroll!(@ 8, 0, $i, $s);
}};
(@ 32, $start:expr, $i:ident, $s:expr) => {{
unroll!(@ 8, $start + 0 * 8, $i, $s);
unroll!(@ 8, $start + 1 * 8, $i, $s);
unroll!(@ 8, $start + 2 * 8, $i, $s);
unroll!(@ 8, $start + 3 * 8, $i, $s);
}};
(@ 8, $start:expr, $i:ident, $s:expr) => {{
unroll!(@ 4, $start, $i, $s);
unroll!(@ 4, $start + 4, $i, $s);
}};
(@ 4, $start:expr, $i:ident, $s:expr) => {{
unroll!(@ 2, $start, $i, $s);
unroll!(@ 2, $start + 2, $i, $s);
}};
(@ 2, $start:expr, $i:ident, $s:expr) => {{
unroll!(@ 1, $start, $i, $s);
unroll!(@ 1, $start + 1, $i, $s);
}};
(@ 1, $start:expr, $i:ident, $s:expr) => {{
let $i = $start;
let _ = $s;
}};
}
const fn generate_crc_table() -> [u32; CRC_TABLE_LEN] { const fn generate_crc_table() -> [u32; CRC_TABLE_LEN] {
let mut table = [0; CRC_TABLE_LEN]; let mut table = [0; CRC_TABLE_LEN];
// NOTE: works on Rust 1.46 let mut i = 0;
//let mut i = 0; while i < CRC_TABLE_LEN {
//while i < CRC_TABLE_LEN {
// table[i] = crc_entry(i as u8) as u32;
//
// i += 1;
//}
unroll!(256, |i| {
table[i] = crc_entry(i as u8) as u32; table[i] = crc_entry(i as u8) as u32;
});
i += 1;
}
table table
} }
@ -86,19 +41,8 @@ const fn generate_crc_table() -> [u32; CRC_TABLE_LEN] {
const fn crc_entry(input: u8) -> u32 { const fn crc_entry(input: u8) -> u32 {
let mut crc = (input as u32) << 24; let mut crc = (input as u32) << 24;
// NOTE: this does not work on Rust 1.33, but *does* on 1.46 let mut i = 0;
//let mut i = 0; while i < 8 {
//while i < 8 {
// if crc & 0x8000_0000 != 0 {
// crc <<= 1;
// crc ^= 0x04c1_1db7;
// } else {
// crc <<= 1;
// }
//
// i += 1;
//}
unroll!(8, |_i| {
let if_condition = crc & 0x8000_0000; let if_condition = crc & 0x8000_0000;
let if_body = (crc << 1) ^ 0x04c1_1db7; let if_body = (crc << 1) ^ 0x04c1_1db7;
let else_body = crc << 1; let else_body = crc << 1;
@ -108,7 +52,8 @@ const fn crc_entry(input: u8) -> u32 {
let condition_table = [else_body, if_body]; let condition_table = [else_body, if_body];
crc = condition_table[(if_condition != 0) as usize]; crc = condition_table[(if_condition != 0) as usize];
}); i += 1;
}
crc crc
} }
@ -148,6 +93,8 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
"Is a directory", "Is a directory",
)); ));
}; };
// Silent the warning as we want to the error message
#[allow(clippy::question_mark)]
if path.metadata().is_err() { if path.metadata().is_err() {
return Err(std::io::Error::new( return Err(std::io::Error::new(
io::ErrorKind::NotFound, io::ErrorKind::NotFound,

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_comm" name = "uu_comm"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "comm ~ (uutils) compare sorted inputs" description = "comm ~ (uutils) compare sorted inputs"
@ -17,13 +17,12 @@ path = "src/comm.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "comm" name = "comm"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_cp" name = "uu_cp"
version = "0.0.7" version = "0.0.8"
authors = [ authors = [
"Jordy Dickinson <jordy.dickinson@gmail.com>", "Jordy Dickinson <jordy.dickinson@gmail.com>",
"Joshua S. Miller <jsmiller@uchicago.edu>", "Joshua S. Miller <jsmiller@uchicago.edu>",
@ -24,8 +24,8 @@ filetime = "0.2"
libc = "0.2.85" libc = "0.2.85"
quick-error = "1.2.3" quick-error = "1.2.3"
selinux = { version="0.2.3", optional=true } selinux = { version="0.2.3", optional=true }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "perms", "mode"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms", "mode"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
walkdir = "2.2" walkdir = "2.2"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
@ -47,5 +47,4 @@ feat_selinux = ["selinux"]
feat_acl = ["exacl"] feat_acl = ["exacl"]
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -49,6 +49,7 @@ use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use uucore::backup_control::{self, BackupMode}; use uucore::backup_control::{self, BackupMode};
use uucore::error::{set_exit_code, ExitCode, UError, UResult};
use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode};
use walkdir::WalkDir; use walkdir::WalkDir;
@ -105,6 +106,12 @@ quick_error! {
} }
} }
impl UError for Error {
fn code(&self) -> i32 {
EXIT_ERR
}
}
/// Continue next iteration of loop if result of expression is error /// Continue next iteration of loop if result of expression is error
macro_rules! or_continue( macro_rules! or_continue(
($expr:expr) => (match $expr { ($expr:expr) => (match $expr {
@ -220,7 +227,6 @@ pub struct Options {
static ABOUT: &str = "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY."; static ABOUT: &str = "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.";
static LONG_HELP: &str = ""; static LONG_HELP: &str = "";
static EXIT_OK: i32 = 0;
static EXIT_ERR: i32 = 1; static EXIT_ERR: i32 = 1;
fn usage() -> String { fn usage() -> String {
@ -446,7 +452,8 @@ pub fn uu_app() -> App<'static, 'static> {
.multiple(true)) .multiple(true))
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage(); let usage = usage();
let matches = uu_app() let matches = uu_app()
.after_help(&*format!( .after_help(&*format!(
@ -457,11 +464,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.usage(&usage[..]) .usage(&usage[..])
.get_matches_from(args); .get_matches_from(args);
let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches)); let options = Options::from_matches(&matches)?;
if options.overwrite == OverwriteMode::NoClobber && options.backup != BackupMode::NoBackup { if options.overwrite == OverwriteMode::NoClobber && options.backup != BackupMode::NoBackup {
show_usage_error!("options --backup and --no-clobber are mutually exclusive"); show_usage_error!("options --backup and --no-clobber are mutually exclusive");
return 1; return Err(ExitCode(EXIT_ERR).into());
} }
let paths: Vec<String> = matches let paths: Vec<String> = matches
@ -469,7 +476,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.map(|v| v.map(ToString::to_string).collect()) .map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default(); .unwrap_or_default();
let (sources, target) = crash_if_err!(EXIT_ERR, parse_path_args(&paths, &options)); let (sources, target) = parse_path_args(&paths, &options)?;
if let Err(error) = copy(&sources, &target, &options) { if let Err(error) = copy(&sources, &target, &options) {
match error { match error {
@ -479,10 +486,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
// Else we caught a fatal bubbled-up error, log it to stderr // Else we caught a fatal bubbled-up error, log it to stderr
_ => show_error!("{}", error), _ => show_error!("{}", error),
}; };
return EXIT_ERR; set_exit_code(EXIT_ERR);
} }
EXIT_OK Ok(())
} }
impl ClobberMode { impl ClobberMode {
@ -1124,7 +1131,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
let xattrs = xattr::list(source)?; let xattrs = xattr::list(source)?;
for attr in xattrs { for attr in xattrs {
if let Some(attr_value) = xattr::get(source, attr.clone())? { if let Some(attr_value) = xattr::get(source, attr.clone())? {
crash_if_err!(EXIT_ERR, xattr::set(dest, attr, &attr_value[..])); xattr::set(dest, attr, &attr_value[..])?;
} }
} }
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_csplit" name = "uu_csplit"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output" description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output"
@ -18,13 +18,12 @@ path = "src/csplit.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
thiserror = "1.0" thiserror = "1.0"
regex = "1.0.0" regex = "1.0.0"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "csplit" name = "csplit"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -320,18 +320,19 @@ impl<'a> SplitWriter<'a> {
let l = line?; let l = line?;
match n.cmp(&(&ln + 1)) { match n.cmp(&(&ln + 1)) {
Ordering::Less => { Ordering::Less => {
if input_iter.add_line_to_buffer(ln, l).is_some() { assert!(
panic!("the buffer is big enough to contain 1 line"); input_iter.add_line_to_buffer(ln, l).is_none(),
} "the buffer is big enough to contain 1 line"
);
ret = Ok(()); ret = Ok(());
break; break;
} }
Ordering::Equal => { Ordering::Equal => {
if !self.options.suppress_matched assert!(
&& input_iter.add_line_to_buffer(ln, l).is_some() self.options.suppress_matched
{ || input_iter.add_line_to_buffer(ln, l).is_none(),
panic!("the buffer is big enough to contain 1 line"); "the buffer is big enough to contain 1 line"
} );
ret = Ok(()); ret = Ok(());
break; break;
} }
@ -378,9 +379,10 @@ impl<'a> SplitWriter<'a> {
match (self.options.suppress_matched, offset) { match (self.options.suppress_matched, offset) {
// no offset, add the line to the next split // no offset, add the line to the next split
(false, 0) => { (false, 0) => {
if input_iter.add_line_to_buffer(ln, l).is_some() { assert!(
panic!("the buffer is big enough to contain 1 line"); input_iter.add_line_to_buffer(ln, l).is_none(),
} "the buffer is big enough to contain 1 line"
);
} }
// a positive offset, some more lines need to be added to the current split // a positive offset, some more lines need to be added to the current split
(false, _) => self.writeln(l)?, (false, _) => self.writeln(l)?,
@ -425,9 +427,10 @@ impl<'a> SplitWriter<'a> {
if !self.options.suppress_matched { if !self.options.suppress_matched {
// add 1 to the buffer size to make place for the matched line // add 1 to the buffer size to make place for the matched line
input_iter.set_size_of_buffer(offset_usize + 1); input_iter.set_size_of_buffer(offset_usize + 1);
if input_iter.add_line_to_buffer(ln, l).is_some() { assert!(
panic!("should be big enough to hold every lines"); input_iter.add_line_to_buffer(ln, l).is_none(),
} "should be big enough to hold every lines"
);
} }
self.finish_split(); self.finish_split();
if input_iter.buffer_len() < offset_usize { if input_iter.buffer_len() < offset_usize {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_cut" name = "uu_cut"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "cut ~ (uutils) display byte/field columns of input lines" description = "cut ~ (uutils) display byte/field columns of input lines"
@ -16,8 +16,8 @@ path = "src/cut.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
memchr = "2" memchr = "2"
bstr = "0.2" bstr = "0.2"
atty = "0.2" atty = "0.2"
@ -27,5 +27,4 @@ name = "cut"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_date" name = "uu_date"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "date ~ (uutils) display or set the current time" description = "date ~ (uutils) display or set the current time"
@ -17,8 +17,8 @@ path = "src/date.rs"
[dependencies] [dependencies]
chrono = "0.4.4" chrono = "0.4.4"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
libc = "0.2" libc = "0.2"
@ -31,5 +31,4 @@ name = "date"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_dd" name = "uu_dd"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "dd ~ (uutils) copy and convert files" description = "dd ~ (uutils) copy and convert files"
@ -33,5 +33,4 @@ name = "dd"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -20,7 +20,7 @@ pub enum ParseError {
MultipleFmtTable, MultipleFmtTable,
MultipleUCaseLCase, MultipleUCaseLCase,
MultipleBlockUnblock, MultipleBlockUnblock,
MultipleExclNoCreat, MultipleExclNoCreate,
FlagNoMatch(String), FlagNoMatch(String),
ConvFlagNoMatch(String), ConvFlagNoMatch(String),
MultiplierStringParseFailure(String), MultiplierStringParseFailure(String),
@ -45,7 +45,7 @@ impl std::fmt::Display for ParseError {
Self::MultipleBlockUnblock => { Self::MultipleBlockUnblock => {
write!(f, "Only one of conv=block or conv=unblock may be specified") write!(f, "Only one of conv=block or conv=unblock may be specified")
} }
Self::MultipleExclNoCreat => { Self::MultipleExclNoCreate => {
write!(f, "Only one ov conv=excl or conv=nocreat may be specified") write!(f, "Only one ov conv=excl or conv=nocreat may be specified")
} }
Self::FlagNoMatch(arg) => { Self::FlagNoMatch(arg) => {
@ -523,14 +523,14 @@ pub fn parse_conv_flag_output(matches: &Matches) -> Result<OConvFlags, ParseErro
if !oconvflags.nocreat { if !oconvflags.nocreat {
oconvflags.excl = true; oconvflags.excl = true;
} else { } else {
return Err(ParseError::MultipleExclNoCreat); return Err(ParseError::MultipleExclNoCreate);
} }
} }
ConvFlag::NoCreat => { ConvFlag::NoCreat => {
if !oconvflags.excl { if !oconvflags.excl {
oconvflags.nocreat = true; oconvflags.nocreat = true;
} else { } else {
return Err(ParseError::MultipleExclNoCreat); return Err(ParseError::MultipleExclNoCreate);
} }
} }
ConvFlag::NoTrunc => oconvflags.notrunc = true, ConvFlag::NoTrunc => oconvflags.notrunc = true,

View file

@ -35,12 +35,11 @@ fn unimplemented_flags_should_error_non_linux() {
} }
} }
if !succeeded.is_empty() { assert!(
panic!( succeeded.is_empty(),
"The following flags did not panic as expected: {:?}", "The following flags did not panic as expected: {:?}",
succeeded succeeded
); );
}
} }
#[test] #[test]
@ -64,12 +63,11 @@ fn unimplemented_flags_should_error() {
} }
} }
if !succeeded.is_empty() { assert!(
panic!( succeeded.is_empty(),
"The following flags did not panic as expected: {:?}", "The following flags did not panic as expected: {:?}",
succeeded succeeded
); );
}
} }
#[test] #[test]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_df" name = "uu_df"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "df ~ (uutils) display file system information" description = "df ~ (uutils) display file system information"
@ -17,9 +17,12 @@ path = "src/df.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
number_prefix = "0.4" number_prefix = "0.4"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["libc", "fsext"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["libc", "fsext"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "df" name = "df"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -6,8 +6,6 @@
// For the full copyright and license information, please view the LICENSE file // For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code. // that was distributed with this source code.
#[macro_use]
extern crate uucore;
use uucore::error::UError; use uucore::error::UError;
use uucore::error::UResult; use uucore::error::UResult;
#[cfg(unix)] #[cfg(unix)]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_dircolors" name = "uu_dircolors"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "dircolors ~ (uutils) display commands to set LS_COLORS" description = "dircolors ~ (uutils) display commands to set LS_COLORS"
@ -17,13 +17,12 @@ path = "src/dircolors.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
glob = "0.3.0" glob = "0.3.0"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "dircolors" name = "dircolors"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_dirname" name = "uu_dirname"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "dirname ~ (uutils) display parent directory of PATHNAME" description = "dirname ~ (uutils) display parent directory of PATHNAME"
@ -17,8 +17,8 @@ path = "src/dirname.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "dirname" name = "dirname"

View file

@ -5,9 +5,6 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::path::Path; use std::path::Path;
use uucore::display::print_verbatim; use uucore::display::print_verbatim;

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_du" name = "uu_du"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "du ~ (uutils) display disk usage" description = "du ~ (uutils) display disk usage"
@ -17,8 +17,8 @@ path = "src/du.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
chrono = "0.4" chrono = "0.4"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version="0.3", features=[] } winapi = { version="0.3", features=[] }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_echo" name = "uu_echo"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "echo ~ (uutils) display TEXT" description = "echo ~ (uutils) display TEXT"
@ -16,8 +16,8 @@ path = "src/echo.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "echo" name = "echo"

View file

@ -6,9 +6,6 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::io::{self, Write}; use std::io::{self, Write};
use std::iter::Peekable; use std::iter::Peekable;

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_env" name = "uu_env"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND" description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND"
@ -18,8 +18,8 @@ path = "src/env.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
rust-ini = "0.17.0" rust-ini = "0.17.0"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "env" name = "env"

79
src/uu/env/src/env.rs vendored
View file

@ -1,13 +1,14 @@
// This file is part of the uutils coreutils package. // This file is part of the uutils coreutils package.
// //
// (c) Jordi Boggiano <j.boggiano@seld.be> // (c) Jordi Boggiano <j.boggiano@seld.be>
// (c) Thomas Queiroz <thomasqueirozb@gmail.com>
// //
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
/* last synced with: env (GNU coreutils) 8.13 */ /* last synced with: env (GNU coreutils) 8.13 */
// spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets // spell-checker:ignore (ToDO) chdir execvp progname subcommand subcommands unsets setenv putenv spawnp
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
@ -23,7 +24,7 @@ use std::io::{self, Write};
use std::iter::Iterator; use std::iter::Iterator;
use std::process::Command; use std::process::Command;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError}; use uucore::error::{UResult, USimpleError, UUsageError};
const USAGE: &str = "env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]"; const USAGE: &str = "env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]";
const AFTER_HELP: &str = "\ const AFTER_HELP: &str = "\
@ -50,7 +51,7 @@ fn print_env(null: bool) {
} }
} }
fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result<bool, i32> { fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> UResult<bool> {
// is it a NAME=VALUE like opt ? // is it a NAME=VALUE like opt ?
if let Some(idx) = opt.find('=') { if let Some(idx) = opt.find('=') {
// yes, so push name, value pair // yes, so push name, value pair
@ -64,17 +65,12 @@ fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result<bool
} }
} }
fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result<(), i32> { fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> UResult<()> {
if opts.null { if opts.null {
eprintln!( Err(UUsageError::new(
"{}: cannot specify --null (-0) with command", 125,
uucore::util_name() "cannot specify --null (-0) with command".to_string(),
); ))
eprintln!(
"Type \"{} --help\" for detailed information",
uucore::execution_phrase()
);
Err(1)
} else { } else {
opts.program.push(opt); opts.program.push(opt);
Ok(()) Ok(())
@ -93,10 +89,8 @@ fn load_config_file(opts: &mut Options) -> UResult<()> {
Ini::load_from_file(file) Ini::load_from_file(file)
}; };
let conf = conf.map_err(|error| { let conf =
show_error!("{}: {}", file.maybe_quote(), error); conf.map_err(|e| USimpleError::new(1, format!("{}: {}", file.maybe_quote(), e)))?;
1
})?;
for (_, prop) in &conf { for (_, prop) in &conf {
// ignore all INI section lines (treat them as comments) // ignore all INI section lines (treat them as comments)
@ -138,7 +132,7 @@ pub fn uu_app() -> App<'static, 'static> {
.long("ignore-environment") .long("ignore-environment")
.help("start with an empty environment")) .help("start with an empty environment"))
.arg(Arg::with_name("chdir") .arg(Arg::with_name("chdir")
.short("c") .short("C") // GNU env compatibility
.long("chdir") .long("chdir")
.takes_value(true) .takes_value(true)
.number_of_values(1) .number_of_values(1)
@ -236,6 +230,14 @@ fn run_env(args: impl uucore::Args) -> UResult<()> {
} }
} }
// GNU env tests this behavior
if opts.program.is_empty() && running_directory.is_some() {
return Err(UUsageError::new(
125,
"must specify command with --chdir (-C)".to_string(),
));
}
// NOTE: we manually set and unset the env vars below rather than using Command::env() to more // NOTE: we manually set and unset the env vars below rather than using Command::env() to more
// easily handle the case where no command is given // easily handle the case where no command is given
@ -251,12 +253,44 @@ fn run_env(args: impl uucore::Args) -> UResult<()> {
// unset specified env vars // unset specified env vars
for name in &opts.unsets { for name in &opts.unsets {
if name.is_empty() || name.contains(0 as char) || name.contains('=') {
return Err(USimpleError::new(
125,
format!("cannot unset {}: Invalid argument", name.quote()),
));
}
env::remove_var(name); env::remove_var(name);
} }
// set specified env vars // set specified env vars
for &(name, val) in &opts.sets { for &(name, val) in &opts.sets {
// FIXME: set_var() panics if name is an empty string /*
* set_var panics if name is an empty string
* set_var internally calls setenv (on unix at least), while GNU env calls putenv instead.
*
* putenv returns successfully if provided with something like "=a" and modifies the environ
* variable to contain "=a" inside it, effectively modifying the process' current environment
* to contain a malformed string in it. Using GNU's implementation, the command `env =a`
* prints out the malformed string and even invokes the child process with that environment.
* This can be seen by using `env -i =a env` or `env -i =a cat /proc/self/environ`
*
* POSIX.1-2017 doesn't seem to mention what to do if the string is malformed (at least
* not in "Chapter 8, Environment Variables" or in the definition for environ and various
* exec*'s or in the description of env in the "Shell & Utilities" volume).
*
* It also doesn't specify any checks for putenv before modifying the environ variable, which
* is likely why glibc doesn't do so. However, the first set_var argument cannot point to
* an empty string or a string containing '='.
*
* There is no benefit in replicating GNU's env behavior, since it will only modify the
* environment in weird ways
*/
if name.is_empty() {
show_warning!("no name specified for value {}", val.quote());
continue;
}
env::set_var(name, val); env::set_var(name, val);
} }
@ -264,7 +298,12 @@ fn run_env(args: impl uucore::Args) -> UResult<()> {
// we need to execute a command // we need to execute a command
let (prog, args) = build_command(&mut opts.program); let (prog, args) = build_command(&mut opts.program);
// FIXME: this should just use execvp() (no fork()) on Unix-like systems /*
* On Unix-like systems Command::status either ends up calling either fork or posix_spawnp
* (which ends up calling clone). Keep using the current process would be ideal, but the
* standard library contains many checks and fail-safes to ensure the process ends up being
* created. This is much simpler than dealing with the hassles of calling execvp directly.
*/
match Command::new(&*prog).args(args).status() { match Command::new(&*prog).args(args).status() {
Ok(exit) if !exit.success() => return Err(exit.code().unwrap().into()), Ok(exit) if !exit.success() => return Err(exit.code().unwrap().into()),
Err(ref err) if err.kind() == io::ErrorKind::NotFound => return Err(127.into()), Err(ref err) if err.kind() == io::ErrorKind::NotFound => return Err(127.into()),

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_expand" name = "uu_expand"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "expand ~ (uutils) convert input tabs to spaces" description = "expand ~ (uutils) convert input tabs to spaces"
@ -17,13 +17,12 @@ path = "src/expand.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
unicode-width = "0.1.5" unicode-width = "0.1.5"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "expand" name = "expand"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_expr" name = "uu_expr"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "expr ~ (uutils) display the value of EXPRESSION" description = "expr ~ (uutils) display the value of EXPRESSION"
@ -20,13 +20,12 @@ libc = "0.2.42"
num-bigint = "0.4.0" num-bigint = "0.4.0"
num-traits = "0.2.14" num-traits = "0.2.14"
onig = "~4.3.2" onig = "~4.3.2"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "expr" name = "expr"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -5,10 +5,8 @@
//* For the full copyright and license information, please view the LICENSE //* For the full copyright and license information, please view the LICENSE
//* file that was distributed with this source code. //* file that was distributed with this source code.
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use uucore::error::{UResult, USimpleError};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
mod syntax_tree; mod syntax_tree;
@ -23,7 +21,8 @@ pub fn uu_app() -> App<'static, 'static> {
.arg(Arg::with_name(HELP).long(HELP)) .arg(Arg::with_name(HELP).long(HELP))
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
@ -32,13 +31,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
// The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)` // The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)`
if maybe_handle_help_or_version(&args) { if maybe_handle_help_or_version(&args) {
0 Ok(())
} else { } else {
let token_strings = args[1..].to_vec(); let token_strings = args[1..].to_vec();
match process_expr(&token_strings) { match process_expr(&token_strings) {
Ok(expr_result) => print_expr_ok(&expr_result), Ok(expr_result) => print_expr_ok(&expr_result),
Err(expr_error) => print_expr_error(&expr_error), Err(expr_error) => Err(USimpleError::new(2, &expr_error)),
} }
} }
} }
@ -49,19 +48,15 @@ fn process_expr(token_strings: &[String]) -> Result<String, String> {
evaluate_ast(maybe_ast) evaluate_ast(maybe_ast)
} }
fn print_expr_ok(expr_result: &str) -> i32 { fn print_expr_ok(expr_result: &str) -> UResult<()> {
println!("{}", expr_result); println!("{}", expr_result);
if expr_result == "0" || expr_result.is_empty() { if expr_result == "0" || expr_result.is_empty() {
1 Err(1.into())
} else { } else {
0 Ok(())
} }
} }
fn print_expr_error(expr_error: &str) -> ! {
crash!(2, "{}", expr_error)
}
fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::AstNode>, String>) -> Result<String, String> { fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::AstNode>, String>) -> Result<String, String> {
maybe_ast.and_then(|ast| ast.evaluate()) maybe_ast.and_then(|ast| ast.evaluate())
} }

View file

@ -44,7 +44,8 @@ on Daniel Lemire's [*Microbenchmarking calls for idealized conditions*][lemire],
which I recommend reading if you want to add benchmarks to `factor`. which I recommend reading if you want to add benchmarks to `factor`.
1. Select a small, self-contained, deterministic component 1. Select a small, self-contained, deterministic component
`gcd` and `table::factor` are good example of such: (`gcd` and `table::factor` are good examples):
- no I/O or access to external data structures ; - no I/O or access to external data structures ;
- no call into other components ; - no call into other components ;
- behavior is deterministic: no RNG, no concurrency, ... ; - behavior is deterministic: no RNG, no concurrency, ... ;
@ -53,16 +54,19 @@ which I recommend reading if you want to add benchmarks to `factor`.
maximizing the numbers of samples we can take in a given time. maximizing the numbers of samples we can take in a given time.
2. Benchmarks are immutable (once merged in `uutils`) 2. Benchmarks are immutable (once merged in `uutils`)
Modifying a benchmark means previously-collected values cannot meaningfully Modifying a benchmark means previously-collected values cannot meaningfully
be compared, silently giving nonsensical results. If you must modify an be compared, silently giving nonsensical results. If you must modify an
existing benchmark, rename it. existing benchmark, rename it.
3. Test common cases 3. Test common cases
We are interested in overall performance, rather than specific edge-cases; We are interested in overall performance, rather than specific edge-cases;
use **reproducibly-randomized inputs**, sampling from either all possible use **reproducibly-randomized inputs**, sampling from either all possible
input values or some subset of interest. input values or some subset of interest.
4. Use [`criterion`], `criterion::black_box`, ... 4. Use [`criterion`], `criterion::black_box`, ...
`criterion` isn't perfect, but it is also much better than ad-hoc `criterion` isn't perfect, but it is also much better than ad-hoc
solutions in each benchmark. solutions in each benchmark.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_factor" name = "uu_factor"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "factor ~ (uutils) display the prime factors of each NUMBER" description = "factor ~ (uutils) display the prime factors of each NUMBER"
@ -15,13 +15,13 @@ edition = "2018"
num-traits = "0.2.13" # used in src/numerics.rs, which is included by build.rs num-traits = "0.2.13" # used in src/numerics.rs, which is included by build.rs
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
coz = { version = "0.1.3", optional = true } coz = { version = "0.1.3", optional = true }
num-traits = "0.2.13" # Needs at least version 0.2.13 for "OverflowingAdd" num-traits = "0.2.13" # Needs at least version 0.2.13 for "OverflowingAdd"
rand = { version = "0.7", features = ["small_rng"] } rand = { version = "0.7", features = ["small_rng"] }
smallvec = { version = "0.6.14, < 1.0" } smallvec = "1.7" # TODO(nicoo): Use `union` feature, requires Rust 1.49 or later.
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" }
uucore_procs = { version=">=0.0.6", package = "uucore_procs", path = "../../uucore_procs" } uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" }
clap = { version = "2.33", features = ["wrap_help"] }
[dev-dependencies] [dev-dependencies]
paste = "0.1.18" paste = "0.1.18"
@ -36,5 +36,4 @@ path = "src/main.rs"
path = "src/cli.rs" path = "src/cli.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -52,7 +52,7 @@ pub fn gcd(mut u: u64, mut v: u64) -> u64 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use quickcheck::quickcheck; use quickcheck::{quickcheck, TestResult};
quickcheck! { quickcheck! {
fn euclidean(a: u64, b: u64) -> bool { fn euclidean(a: u64, b: u64) -> bool {
@ -76,13 +76,12 @@ mod tests {
gcd(0, a) == a gcd(0, a) == a
} }
fn divisor(a: u64, b: u64) -> () { fn divisor(a: u64, b: u64) -> TestResult {
// Test that gcd(a, b) divides a and b, unless a == b == 0 // Test that gcd(a, b) divides a and b, unless a == b == 0
if a == 0 && b == 0 { return; } if a == 0 && b == 0 { return TestResult::discard(); } // restrict test domain to !(a == b == 0)
let g = gcd(a, b); let g = gcd(a, b);
assert_eq!(a % g, 0); TestResult::from_bool( g != 0 && a % g == 0 && b % g == 0 )
assert_eq!(b % g, 0);
} }
fn commutative(a: u64, b: u64) -> bool { fn commutative(a: u64, b: u64) -> bool {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_false" name = "uu_false"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "false ~ (uutils) do nothing and fail" description = "false ~ (uutils) do nothing and fail"
@ -16,8 +16,8 @@ path = "src/false.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "false" name = "false"

View file

@ -5,9 +5,6 @@
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
#[macro_use]
extern crate uucore;
use clap::App; use clap::App;
use uucore::error::UResult; use uucore::error::UResult;

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_fmt" name = "uu_fmt"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "fmt ~ (uutils) reformat each paragraph of input" description = "fmt ~ (uutils) reformat each paragraph of input"
@ -18,13 +18,12 @@ path = "src/fmt.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
unicode-width = "0.1.5" unicode-width = "0.1.5"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "fmt" name = "fmt"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_fold" name = "uu_fold"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "fold ~ (uutils) wrap each line of input" description = "fold ~ (uutils) wrap each line of input"
@ -16,13 +16,12 @@ path = "src/fold.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "fold" name = "fold"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_groups" name = "uu_groups"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "groups ~ (uutils) display group memberships for USERNAME" description = "groups ~ (uutils) display group memberships for USERNAME"
@ -15,10 +15,13 @@ edition = "2018"
path = "src/groups.rs" path = "src/groups.rs"
[dependencies] [dependencies]
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "process"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
[[bin]] [[bin]]
name = "groups" name = "groups"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_hashsum" name = "uu_hashsum"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "hashsum ~ (uutils) display or check input digests" description = "hashsum ~ (uutils) display or check input digests"
@ -27,13 +27,12 @@ sha1 = "0.6.0"
sha2 = "0.6.0" sha2 = "0.6.0"
sha3 = "0.6.0" sha3 = "0.6.0"
blake2b_simd = "0.5.11" blake2b_simd = "0.5.11"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "hashsum" name = "hashsum"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -189,13 +189,31 @@ pub struct DigestWriter<'a> {
/// "\n" before passing input bytes to the [`digest`]. /// "\n" before passing input bytes to the [`digest`].
#[allow(dead_code)] #[allow(dead_code)]
binary: bool, binary: bool,
// TODO This is dead code only on non-Windows operating systems. It
// might be better to use a `#[cfg(windows)]` guard here. /// Whether the previous
#[allow(dead_code)]
was_last_character_carriage_return: bool,
// TODO These are dead code only on non-Windows operating systems.
// It might be better to use a `#[cfg(windows)]` guard here.
} }
impl<'a> DigestWriter<'a> { impl<'a> DigestWriter<'a> {
pub fn new(digest: &'a mut Box<dyn Digest>, binary: bool) -> DigestWriter { pub fn new(digest: &'a mut Box<dyn Digest>, binary: bool) -> DigestWriter {
DigestWriter { digest, binary } let was_last_character_carriage_return = false;
DigestWriter {
digest,
binary,
was_last_character_carriage_return,
}
}
pub fn finalize(&mut self) -> bool {
if self.was_last_character_carriage_return {
self.digest.input(&[b'\r']);
true
} else {
false
}
} }
} }
@ -213,22 +231,40 @@ impl<'a> Write for DigestWriter<'a> {
return Ok(buf.len()); return Ok(buf.len());
} }
// In Windows text mode, replace each occurrence of "\r\n" // The remaining code handles Windows text mode, where we must
// with "\n". // replace each occurrence of "\r\n" with "\n".
// //
// Find all occurrences of "\r\n", inputting the slice just // First, if the last character written was "\r" and the first
// before the "\n" in the previous instance of "\r\n" and // character in the current buffer to write is not "\n", then we
// the beginning of this "\r\n". // need to write the "\r" that we buffered from the previous
// // call to `write()`.
// FIXME This fails if one call to `write()` ends with the
// "\r" and the next call to `write()` begins with the "\n".
let n = buf.len(); let n = buf.len();
if self.was_last_character_carriage_return && n > 0 && buf[0] != b'\n' {
self.digest.input(&[b'\r']);
}
// Next, find all occurrences of "\r\n", inputting the slice
// just before the "\n" in the previous instance of "\r\n" and
// the beginning of this "\r\n".
let mut i_prev = 0; let mut i_prev = 0;
for i in memmem::find_iter(buf, b"\r\n") { for i in memmem::find_iter(buf, b"\r\n") {
self.digest.input(&buf[i_prev..i]); self.digest.input(&buf[i_prev..i]);
i_prev = i + 1; i_prev = i + 1;
} }
// Finally, check whether the last character is "\r". If so,
// buffer it until we know that the next character is not "\n",
// which can only be known on the next call to `write()`.
//
// This all assumes that `write()` will be called on adjacent
// blocks of the input.
if n > 0 && buf[n - 1] == b'\r' {
self.was_last_character_carriage_return = true;
self.digest.input(&buf[i_prev..n - 1]);
} else {
self.was_last_character_carriage_return = false;
self.digest.input(&buf[i_prev..n]); self.digest.input(&buf[i_prev..n]);
}
// Even though we dropped a "\r" for each "\r\n" we found, we // Even though we dropped a "\r" for each "\r\n" we found, we
// still report the number of bytes written as `n`. This is // still report the number of bytes written as `n`. This is
@ -243,3 +279,36 @@ impl<'a> Write for DigestWriter<'a> {
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod tests {
/// Test for replacing a "\r\n" sequence with "\n" when the "\r" is
/// at the end of one block and the "\n" is at the beginning of the
/// next block, when reading in blocks.
#[cfg(windows)]
#[test]
fn test_crlf_across_blocks() {
use std::io::Write;
use crate::digest::Digest;
use crate::digest::DigestWriter;
// Writing "\r" in one call to `write()`, and then "\n" in another.
let mut digest = Box::new(md5::Context::new()) as Box<dyn Digest>;
let mut writer_crlf = DigestWriter::new(&mut digest, false);
writer_crlf.write_all(&[b'\r']).unwrap();
writer_crlf.write_all(&[b'\n']).unwrap();
writer_crlf.finalize();
let result_crlf = digest.result_str();
// We expect "\r\n" to be replaced with "\n" in text mode on Windows.
let mut digest = Box::new(md5::Context::new()) as Box<dyn Digest>;
let mut writer_lf = DigestWriter::new(&mut digest, false);
writer_lf.write_all(&[b'\n']).unwrap();
writer_lf.finalize();
let result_lf = digest.result_str();
assert_eq!(result_crlf, result_lf);
}
}

View file

@ -611,8 +611,16 @@ fn digest_reader<T: Read>(
// If `binary` is `false` and the operating system is Windows, then // If `binary` is `false` and the operating system is Windows, then
// `DigestWriter` replaces "\r\n" with "\n" before it writes the // `DigestWriter` replaces "\r\n" with "\n" before it writes the
// bytes into `digest`. Otherwise, it just inserts the bytes as-is. // bytes into `digest`. Otherwise, it just inserts the bytes as-is.
//
// In order to support replacing "\r\n", we must call `finalize()`
// in order to support the possibility that the last character read
// from the reader was "\r". (This character gets buffered by
// `DigestWriter` and only written if the following character is
// "\n". But when "\r" is the last character read, we need to force
// it to be written.)
let mut digest_writer = DigestWriter::new(digest, binary); let mut digest_writer = DigestWriter::new(digest, binary);
std::io::copy(reader, &mut digest_writer)?; std::io::copy(reader, &mut digest_writer)?;
digest_writer.finalize();
if digest.output_bits() > 0 { if digest.output_bits() > 0 {
Ok(digest.result_str()) Ok(digest.result_str())

View file

@ -0,0 +1,41 @@
# Benchmarking to measure performance
To compare the performance of the `uutils` version of `head` with the
GNU version of `head`, you can use a benchmarking tool like
[hyperfine][0]. On Ubuntu 18.04 or later, you can install `hyperfine` by
running
sudo apt-get install hyperfine
Next, build the `head` binary under the release profile:
cargo build --release -p uu_head
Now, get a text file to test `head` on. I used the *Complete Works of
William Shakespeare*, which is in the public domain in the United States
and most other parts of the world.
wget -O shakespeare.txt https://www.gutenberg.org/files/100/100-0.txt
This particular file has about 170,000 lines, each of which is no longer
than 96 characters:
$ wc -lL shakespeare.txt
170592 96 shakespeare.txt
You could use files of different shapes and sizes to test the
performance of `head` in different situations. For a larger file, you
could download a [database dump of Wikidata][1] or some related files
that the Wikimedia project provides. For example, [this file][2]
contains about 130 million lines.
Finally, you can compare the performance of the two versions of `head`
by running, for example,
hyperfine \
"head -n 100000 shakespeare.txt" \
"target/release/head -n 100000 shakespeare.txt"
[0]: https://github.com/sharkdp/hyperfine
[1]: https://www.wikidata.org/wiki/Wikidata:Database_download
[2]: https://dumps.wikimedia.org/wikidatawiki/20211001/wikidatawiki-20211001-pages-logging.xml.gz

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_head" name = "uu_head"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "head ~ (uutils) display the first lines of input" description = "head ~ (uutils) display the first lines of input"
@ -16,13 +16,13 @@ path = "src/head.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["ringbuffer"] } memchr = "2"
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["ringbuffer"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "head" name = "head"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -3,23 +3,24 @@
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
// spell-checker:ignore (vars) zlines // spell-checker:ignore (vars) zlines BUFWRITER
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ffi::OsString; use std::ffi::OsString;
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::{crash, show_error_custom_description}; use uucore::error::{UResult, USimpleError};
use uucore::show_error_custom_description;
const EXIT_FAILURE: i32 = 1;
const EXIT_SUCCESS: i32 = 0;
const BUF_SIZE: usize = 65536; const BUF_SIZE: usize = 65536;
/// The capacity in bytes for buffered writers.
const BUFWRITER_CAPACITY: usize = 16_384; // 16 kilobytes
const ABOUT: &str = "\ const ABOUT: &str = "\
Print the first 10 lines of each FILE to standard output.\n\ Print the first 10 lines of each FILE to standard output.\n\
With more than one FILE, precede each with a header giving the file name.\n\ With more than one FILE, precede each with a header giving the file name.\n\
\n\
With no FILE, or when FILE is -, read standard input.\n\ With no FILE, or when FILE is -, read standard input.\n\
\n\ \n\
Mandatory arguments to long flags are mandatory for short flags too.\ Mandatory arguments to long flags are mandatory for short flags too.\
@ -36,10 +37,10 @@ mod options {
} }
mod lines; mod lines;
mod parse; mod parse;
mod split;
mod take; mod take;
use lines::zlines; use lines::zlines;
use take::take_all_but; use take::take_all_but;
use take::take_lines;
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
App::new(uucore::util_name()) App::new(uucore::util_name())
@ -108,6 +109,12 @@ enum Modes {
Bytes(usize), Bytes(usize),
} }
impl Default for Modes {
fn default() -> Self {
Modes::Lines(10)
}
}
fn parse_mode<F>(src: &str, closure: F) -> Result<(Modes, bool), String> fn parse_mode<F>(src: &str, closure: F) -> Result<(Modes, bool), String>
where where
F: FnOnce(usize) -> Modes, F: FnOnce(usize) -> Modes,
@ -144,7 +151,7 @@ fn arg_iterate<'a>(
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Default)]
struct HeadOptions { struct HeadOptions {
pub quiet: bool, pub quiet: bool,
pub verbose: bool, pub verbose: bool,
@ -155,22 +162,11 @@ struct HeadOptions {
} }
impl HeadOptions { impl HeadOptions {
pub fn new() -> HeadOptions {
HeadOptions {
quiet: false,
verbose: false,
zeroed: false,
all_but_last: false,
mode: Modes::Lines(10),
files: Vec::new(),
}
}
///Construct options from matches ///Construct options from matches
pub fn get_from(args: impl uucore::Args) -> Result<Self, String> { pub fn get_from(args: impl uucore::Args) -> Result<Self, String> {
let matches = uu_app().get_matches_from(arg_iterate(args)?); let matches = uu_app().get_matches_from(arg_iterate(args)?);
let mut options = HeadOptions::new(); let mut options: HeadOptions = Default::default();
options.quiet = matches.is_present(options::QUIET_NAME); options.quiet = matches.is_present(options::QUIET_NAME);
options.verbose = matches.is_present(options::VERBOSE_NAME); options.verbose = matches.is_present(options::VERBOSE_NAME);
@ -197,12 +193,6 @@ impl HeadOptions {
Ok(options) Ok(options)
} }
} }
// to make clippy shut up
impl Default for HeadOptions {
fn default() -> Self {
Self::new()
}
}
fn read_n_bytes<R>(input: R, n: usize) -> std::io::Result<()> fn read_n_bytes<R>(input: R, n: usize) -> std::io::Result<()>
where where
@ -221,26 +211,18 @@ where
} }
fn read_n_lines(input: &mut impl std::io::BufRead, n: usize, zero: bool) -> std::io::Result<()> { fn read_n_lines(input: &mut impl std::io::BufRead, n: usize, zero: bool) -> std::io::Result<()> {
if n == 0 { // Read the first `n` lines from the `input` reader.
return Ok(()); let separator = if zero { b'\0' } else { b'\n' };
} let mut reader = take_lines(input, n, separator);
// Write those bytes to `stdout`.
let stdout = std::io::stdout(); let stdout = std::io::stdout();
let mut stdout = stdout.lock(); let stdout = stdout.lock();
let mut lines = 0usize; let mut writer = BufWriter::with_capacity(BUFWRITER_CAPACITY, stdout);
split::walk_lines(input, zero, |e| match e {
split::Event::Data(dat) => { io::copy(&mut reader, &mut writer)?;
stdout.write_all(dat)?;
Ok(true) Ok(())
}
split::Event::Line => {
lines += 1;
if lines == n {
Ok(false)
} else {
Ok(true)
}
}
})
} }
fn read_but_last_n_bytes(input: &mut impl std::io::BufRead, n: usize) -> std::io::Result<()> { fn read_but_last_n_bytes(input: &mut impl std::io::BufRead, n: usize) -> std::io::Result<()> {
@ -384,7 +366,7 @@ fn head_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Resul
} }
} }
fn uu_head(options: &HeadOptions) -> Result<(), u32> { fn uu_head(options: &HeadOptions) -> UResult<()> {
let mut error_count = 0; let mut error_count = 0;
let mut first = true; let mut first = true;
for file in &options.files { for file in &options.files {
@ -457,23 +439,21 @@ fn uu_head(options: &HeadOptions) -> Result<(), u32> {
first = false; first = false;
} }
if error_count > 0 { if error_count > 0 {
Err(error_count) Err(USimpleError::new(1, ""))
} else { } else {
Ok(()) Ok(())
} }
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = match HeadOptions::get_from(args) { let args = match HeadOptions::get_from(args) {
Ok(o) => o, Ok(o) => o,
Err(s) => { Err(s) => {
crash!(EXIT_FAILURE, "{}", s); return Err(USimpleError::new(1, s));
} }
}; };
match uu_head(&args) { uu_head(&args)
Ok(_) => EXIT_SUCCESS,
Err(_) => EXIT_FAILURE,
}
} }
#[cfg(test)] #[cfg(test)]
@ -523,17 +503,13 @@ mod tests {
assert!(options("-c IsThisJustFantasy").is_err()); assert!(options("-c IsThisJustFantasy").is_err());
} }
#[test] #[test]
#[allow(clippy::bool_comparison)]
fn test_options_correct_defaults() { fn test_options_correct_defaults() {
let opts = HeadOptions::new(); let opts: HeadOptions = Default::default();
let opts2: HeadOptions = Default::default();
assert_eq!(opts, opts2); assert!(!opts.verbose);
assert!(!opts.quiet);
assert!(opts.verbose == false); assert!(!opts.zeroed);
assert!(opts.quiet == false); assert!(!opts.all_but_last);
assert!(opts.zeroed == false);
assert!(opts.all_but_last == false);
assert_eq!(opts.mode, Modes::Lines(10)); assert_eq!(opts.mode, Modes::Lines(10));
assert!(opts.files.is_empty()); assert!(opts.files.is_empty());
} }

View file

@ -1,60 +0,0 @@
#[derive(Debug)]
pub enum Event<'a> {
Data(&'a [u8]),
Line,
}
/// Loops over the lines read from a BufRead.
/// # Arguments
/// * `input` the ReadBuf to read from
/// * `zero` whether to use 0u8 as a line delimiter
/// * `on_event` a closure receiving some bytes read in a slice, or
/// event signalling a line was just read.
/// this is guaranteed to be signalled *directly* after the
/// slice containing the (CR on win)LF / 0 is passed
///
/// Return whether to continue
pub fn walk_lines<F>(
input: &mut impl std::io::BufRead,
zero: bool,
mut on_event: F,
) -> std::io::Result<()>
where
F: FnMut(Event) -> std::io::Result<bool>,
{
let mut buffer = [0u8; super::BUF_SIZE];
loop {
let read = loop {
match input.read(&mut buffer) {
Ok(n) => break n,
Err(e) => match e.kind() {
std::io::ErrorKind::Interrupted => {}
_ => return Err(e),
},
}
};
if read == 0 {
return Ok(());
}
let mut base = 0usize;
for (i, byte) in buffer[..read].iter().enumerate() {
match byte {
b'\n' if !zero => {
on_event(Event::Data(&buffer[base..=i]))?;
base = i + 1;
if !on_event(Event::Line)? {
return Ok(());
}
}
0u8 if zero => {
on_event(Event::Data(&buffer[base..=i]))?;
base = i + 1;
if !on_event(Event::Line)? {
return Ok(());
}
}
_ => {}
}
}
on_event(Event::Data(&buffer[base..read]))?;
}
}

View file

@ -1,4 +1,8 @@
//! Take all but the last elements of an iterator. //! Take all but the last elements of an iterator.
use std::io::Read;
use memchr::memchr_iter;
use uucore::ringbuffer::RingBuffer; use uucore::ringbuffer::RingBuffer;
/// Create an iterator over all but the last `n` elements of `iter`. /// Create an iterator over all but the last `n` elements of `iter`.
@ -58,10 +62,63 @@ where
} }
} }
/// Like `std::io::Take`, but for lines instead of bytes.
///
/// This struct is generally created by calling [`take_lines`] on a
/// reader. Please see the documentation of [`take`] for more
/// details.
pub struct TakeLines<T> {
inner: T,
limit: usize,
separator: u8,
}
impl<T: Read> Read for TakeLines<T> {
/// Read bytes from a buffer up to the requested number of lines.
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.limit == 0 {
return Ok(0);
}
match self.inner.read(buf) {
Ok(0) => Ok(0),
Ok(n) => {
for i in memchr_iter(self.separator, &buf[..n]) {
self.limit -= 1;
if self.limit == 0 {
return Ok(i + 1);
}
}
Ok(n)
}
Err(e) => Err(e),
}
}
}
/// Create an adaptor that will read at most `limit` lines from a given reader.
///
/// This function returns a new instance of `Read` that will read at
/// most `limit` lines, after which it will always return EOF
/// (`Ok(0)`).
///
/// The `separator` defines the character to interpret as the line
/// ending. For the usual notion of "line", set this to `b'\n'`.
pub fn take_lines<R>(reader: R, limit: usize, separator: u8) -> TakeLines<R> {
TakeLines {
inner: reader,
limit,
separator,
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io::BufRead;
use std::io::BufReader;
use crate::take::take_all_but; use crate::take::take_all_but;
use crate::take::take_lines;
#[test] #[test]
fn test_fewer_elements() { fn test_fewer_elements() {
@ -90,4 +147,33 @@ mod tests {
assert_eq!(Some(&2), iter.next()); assert_eq!(Some(&2), iter.next());
assert_eq!(None, iter.next()); assert_eq!(None, iter.next());
} }
#[test]
fn test_zero_lines() {
let input_reader = std::io::Cursor::new("a\nb\nc\n");
let output_reader = BufReader::new(take_lines(input_reader, 0, b'\n'));
let mut iter = output_reader.lines().map(|l| l.unwrap());
assert_eq!(None, iter.next());
}
#[test]
fn test_fewer_lines() {
let input_reader = std::io::Cursor::new("a\nb\nc\n");
let output_reader = BufReader::new(take_lines(input_reader, 2, b'\n'));
let mut iter = output_reader.lines().map(|l| l.unwrap());
assert_eq!(Some(String::from("a")), iter.next());
assert_eq!(Some(String::from("b")), iter.next());
assert_eq!(None, iter.next());
}
#[test]
fn test_more_lines() {
let input_reader = std::io::Cursor::new("a\nb\nc\n");
let output_reader = BufReader::new(take_lines(input_reader, 4, b'\n'));
let mut iter = output_reader.lines().map(|l| l.unwrap());
assert_eq!(Some(String::from("a")), iter.next());
assert_eq!(Some(String::from("b")), iter.next());
assert_eq!(Some(String::from("c")), iter.next());
assert_eq!(None, iter.next());
}
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_hostid" name = "uu_hostid"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "hostid ~ (uutils) display the numeric identifier of the current host" description = "hostid ~ (uutils) display the numeric identifier of the current host"
@ -17,8 +17,8 @@ path = "src/hostid.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "hostid" name = "hostid"

View file

@ -7,9 +7,6 @@
// spell-checker:ignore (ToDO) gethostid // spell-checker:ignore (ToDO) gethostid
#[macro_use]
extern crate uucore;
use clap::{crate_version, App}; use clap::{crate_version, App};
use libc::c_long; use libc::c_long;
use uucore::error::UResult; use uucore::error::UResult;

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_hostname" name = "uu_hostname"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "hostname ~ (uutils) display or set the host name of the current host" description = "hostname ~ (uutils) display or set the host name of the current host"
@ -18,10 +18,13 @@ path = "src/hostname.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
hostname = { version = "0.3", features = ["set"] } hostname = { version = "0.3", features = ["set"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["wide"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["wide"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
winapi = { version="0.3", features=["sysinfoapi", "winsock2"] } winapi = { version="0.3", features=["sysinfoapi", "winsock2"] }
[[bin]] [[bin]]
name = "hostname" name = "hostname"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs", "winapi"]

View file

@ -7,9 +7,6 @@
// spell-checker:ignore (ToDO) MAKEWORD addrs hashset // spell-checker:ignore (ToDO) MAKEWORD addrs hashset
#[macro_use]
extern crate uucore;
use std::collections::hash_set::HashSet; use std::collections::hash_set::HashSet;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use std::str; use std::str;

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_id" name = "uu_id"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "id ~ (uutils) display user and group information for USER" description = "id ~ (uutils) display user and group information for USER"
@ -16,8 +16,8 @@ path = "src/id.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "process"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
selinux = { version="0.2.1", optional = true } selinux = { version="0.2.1", optional = true }
[[bin]] [[bin]]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_install" name = "uu_install"
version = "0.0.7" version = "0.0.8"
authors = [ authors = [
"Ben Eills <ben@beneills.com>", "Ben Eills <ben@beneills.com>",
"uutils developers", "uutils developers",
@ -22,8 +22,8 @@ clap = { version = "2.33", features = ["wrap_help"] }
filetime = "0.2" filetime = "0.2"
file_diff = "1.0.0" file_diff = "1.0.0"
libc = ">= 0.2" libc = ">= 0.2"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["mode", "perms", "entries"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode", "perms", "entries"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[dev-dependencies] [dev-dependencies]
time = "0.1.40" time = "0.1.40"

View file

@ -461,6 +461,8 @@ fn standard(mut paths: Vec<String>, b: Behavior) -> UResult<()> {
return Err(InstallError::CreateDirFailed(parent.to_path_buf(), e).into()); return Err(InstallError::CreateDirFailed(parent.to_path_buf(), e).into());
} }
// Silent the warning as we want to the error message
#[allow(clippy::question_mark)]
if mode::chmod(parent, b.mode()).is_err() { if mode::chmod(parent, b.mode()).is_err() {
return Err(InstallError::ChmodFailed(parent.to_path_buf()).into()); return Err(InstallError::ChmodFailed(parent.to_path_buf()).into());
} }
@ -583,6 +585,8 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
} }
} }
// Silent the warning as we want to the error message
#[allow(clippy::question_mark)]
if mode::chmod(to, b.mode()).is_err() { if mode::chmod(to, b.mode()).is_err() {
return Err(InstallError::ChmodFailed(to.to_path_buf()).into()); return Err(InstallError::ChmodFailed(to.to_path_buf()).into());
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_join" name = "uu_join"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "join ~ (uutils) merge lines from inputs with matching join fields" description = "join ~ (uutils) merge lines from inputs with matching join fields"
@ -16,13 +16,12 @@ path = "src/join.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "join" name = "join"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_kill" name = "uu_kill"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "kill ~ (uutils) send a signal to a process" description = "kill ~ (uutils) send a signal to a process"
@ -17,8 +17,8 @@ path = "src/kill.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["signals"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["signals"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "kill" name = "kill"

View file

@ -158,18 +158,13 @@ fn print_signal(signal_name_or_value: &str) -> UResult<()> {
} }
fn print_signals() { fn print_signals() {
let mut pos = 0;
for (idx, signal) in ALL_SIGNALS.iter().enumerate() { for (idx, signal) in ALL_SIGNALS.iter().enumerate() {
pos += signal.len(); if idx > 0 {
print!("{}", signal);
if idx > 0 && pos > 73 {
println!();
pos = 0;
} else {
pos += 1;
print!(" "); print!(" ");
} }
print!("{}", signal);
} }
println!();
} }
fn list(arg: Option<String>) -> UResult<()> { fn list(arg: Option<String>) -> UResult<()> {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_link" name = "uu_link"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "link ~ (uutils) create a hard (file system) link to FILE" description = "link ~ (uutils) create a hard (file system) link to FILE"
@ -16,8 +16,8 @@ path = "src/link.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
[[bin]] [[bin]]
@ -25,5 +25,4 @@ name = "link"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_ln" name = "uu_ln"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "ln ~ (uutils) create a (file system) link to TARGET" description = "ln ~ (uutils) create a (file system) link to TARGET"
@ -17,8 +17,8 @@ path = "src/ln.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "ln" name = "ln"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_logname" name = "uu_logname"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "logname ~ (uutils) display the login name of the current user" description = "logname ~ (uutils) display the login name of the current user"
@ -17,9 +17,12 @@ path = "src/logname.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "logname" name = "logname"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_ls" name = "uu_ls"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "ls ~ (uutils) display directory contents" description = "ls ~ (uutils) display directory contents"
@ -24,9 +24,10 @@ termsize = "0.1.6"
globset = "0.4.6" globset = "0.4.6"
lscolors = { version = "0.7.1", features = ["ansi_term"] } lscolors = { version = "0.7.1", features = ["ansi_term"] }
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] }
uucore_procs = { version=">=0.0.6", package = "uucore_procs", path = "../../uucore_procs" } uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" }
once_cell = "1.7.2" once_cell = "1.7.2"
atty = "0.2" atty = "0.2"
selinux = { version="0.2.1", optional = true }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
lazy_static = "1.4.0" lazy_static = "1.4.0"
@ -35,6 +36,8 @@ lazy_static = "1.4.0"
name = "ls" name = "ls"
path = "src/main.rs" path = "src/main.rs"
[features]
feat_selinux = ["selinux"]
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -50,6 +50,11 @@ use unicode_width::UnicodeWidthStr;
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR}; use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
use uucore::{fs::display_permissions, version_cmp::version_cmp}; use uucore::{fs::display_permissions, version_cmp::version_cmp};
#[cfg(not(feature = "selinux"))]
static CONTEXT_HELP_TEXT: &str = "print any security context of each file (not enabled)";
#[cfg(feature = "selinux")]
static CONTEXT_HELP_TEXT: &str = "print any security context of each file";
fn usage() -> String { fn usage() -> String {
format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase()) format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
} }
@ -129,6 +134,7 @@ pub mod options {
pub static FULL_TIME: &str = "full-time"; pub static FULL_TIME: &str = "full-time";
pub static HIDE: &str = "hide"; pub static HIDE: &str = "hide";
pub static IGNORE: &str = "ignore"; pub static IGNORE: &str = "ignore";
pub static CONTEXT: &str = "context";
} }
const DEFAULT_TERM_WIDTH: u16 = 80; const DEFAULT_TERM_WIDTH: u16 = 80;
@ -239,6 +245,8 @@ struct Config {
quoting_style: QuotingStyle, quoting_style: QuotingStyle,
indicator_style: IndicatorStyle, indicator_style: IndicatorStyle,
time_style: TimeStyle, time_style: TimeStyle,
context: bool,
selinux_supported: bool,
} }
// Fields that can be removed or added to the long format // Fields that can be removed or added to the long format
@ -250,9 +258,18 @@ struct LongFormat {
numeric_uid_gid: bool, numeric_uid_gid: bool,
} }
struct PaddingCollection {
longest_link_count_len: usize,
longest_uname_len: usize,
longest_group_len: usize,
longest_context_len: usize,
longest_size_len: usize,
}
impl Config { impl Config {
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
fn from(options: &clap::ArgMatches) -> UResult<Config> { fn from(options: &clap::ArgMatches) -> UResult<Config> {
let context = options.is_present(options::CONTEXT);
let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) { let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) {
( (
match format_ { match format_ {
@ -596,6 +613,17 @@ impl Config {
quoting_style, quoting_style,
indicator_style, indicator_style,
time_style, time_style,
context,
selinux_supported: {
#[cfg(feature = "selinux")]
{
selinux::kernel_support() != selinux::KernelSupport::Unsupported
}
#[cfg(not(feature = "selinux"))]
{
false
}
},
}) })
} }
} }
@ -1083,6 +1111,9 @@ only ignore '.' and '..'.",
.long(options::COLOR) .long(options::COLOR)
.help("Color output based on file type.") .help("Color output based on file type.")
.takes_value(true) .takes_value(true)
.possible_values(&[
"always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none",
])
.require_equals(true) .require_equals(true)
.min_values(0), .min_values(0),
) )
@ -1157,6 +1188,12 @@ only ignore '.' and '..'.",
.overrides_with(options::FULL_TIME) .overrides_with(options::FULL_TIME)
.help("like -l --time-style=full-iso"), .help("like -l --time-style=full-iso"),
) )
.arg(
Arg::with_name(options::CONTEXT)
.short("Z")
.long(options::CONTEXT)
.help(CONTEXT_HELP_TEXT),
)
// Positional arguments // Positional arguments
.arg( .arg(
Arg::with_name(options::PATHS) Arg::with_name(options::PATHS)
@ -1181,6 +1218,7 @@ struct PathData {
// PathBuf that all above data corresponds to // PathBuf that all above data corresponds to
p_buf: PathBuf, p_buf: PathBuf,
must_dereference: bool, must_dereference: bool,
security_context: String,
} }
impl PathData { impl PathData {
@ -1224,12 +1262,19 @@ impl PathData {
None => OnceCell::new(), None => OnceCell::new(),
}; };
let security_context = if config.context {
get_security_context(config, &p_buf, must_dereference)
} else {
String::new()
};
Self { Self {
md: OnceCell::new(), md: OnceCell::new(),
ft, ft,
display_name, display_name,
p_buf, p_buf,
must_dereference, must_dereference,
security_context,
} }
} }
@ -1398,7 +1443,7 @@ fn get_metadata(entry: &Path, dereference: bool) -> std::io::Result<Metadata> {
} }
fn display_dir_entry_size(entry: &PathData, config: &Config) -> (usize, usize, usize, usize) { fn display_dir_entry_size(entry: &PathData, config: &Config) -> (usize, usize, usize, usize) {
// TODO: Cache/memoize the display_* results so we don't have to recalculate them. // TODO: Cache/memorize the display_* results so we don't have to recalculate them.
if let Some(md) = entry.md() { if let Some(md) = entry.md() {
( (
display_symlink_count(md).len(), display_symlink_count(md).len(),
@ -1411,31 +1456,40 @@ fn display_dir_entry_size(entry: &PathData, config: &Config) -> (usize, usize, u
} }
} }
fn pad_left(string: String, count: usize) -> String { fn pad_left(string: &str, count: usize) -> String {
format!("{:>width$}", string, width = count) format!("{:>width$}", string, width = count)
} }
fn pad_right(string: String, count: usize) -> String { fn pad_right(string: &str, count: usize) -> String {
format!("{:<width$}", string, width = count) format!("{:<width$}", string, width = count)
} }
fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout>) { fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout>) {
// `-Z`, `--context`:
// Display the SELinux security context or '?' if none is found. When used with the `-l`
// option, print the security context to the left of the size column.
if config.format == Format::Long { if config.format == Format::Long {
let ( let (
mut longest_link_count_len, mut longest_link_count_len,
mut longest_uname_len, mut longest_uname_len,
mut longest_group_len, mut longest_group_len,
mut longest_context_len,
mut longest_size_len, mut longest_size_len,
) = (1, 1, 1, 1); ) = (1, 1, 1, 1, 1);
let mut total_size = 0; let mut total_size = 0;
for item in items { for item in items {
let context_len = item.security_context.len();
let (link_count_len, uname_len, group_len, size_len) = let (link_count_len, uname_len, group_len, size_len) =
display_dir_entry_size(item, config); display_dir_entry_size(item, config);
longest_link_count_len = link_count_len.max(longest_link_count_len); longest_link_count_len = link_count_len.max(longest_link_count_len);
longest_size_len = size_len.max(longest_size_len); longest_size_len = size_len.max(longest_size_len);
longest_uname_len = uname_len.max(longest_uname_len); longest_uname_len = uname_len.max(longest_uname_len);
longest_group_len = group_len.max(longest_group_len); longest_group_len = group_len.max(longest_group_len);
if config.context {
longest_context_len = context_len.max(longest_context_len);
}
longest_size_len = size_len.max(longest_size_len); longest_size_len = size_len.max(longest_size_len);
total_size += item.md().map_or(0, |md| get_block_size(md, config)); total_size += item.md().map_or(0, |md| get_block_size(md, config));
} }
@ -1447,16 +1501,31 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
for item in items { for item in items {
display_item_long( display_item_long(
item, item,
PaddingCollection {
longest_link_count_len, longest_link_count_len,
longest_uname_len, longest_uname_len,
longest_group_len, longest_group_len,
longest_context_len,
longest_size_len, longest_size_len,
},
config, config,
out, out,
); );
} }
} else { } else {
let names = items.iter().filter_map(|i| display_file_name(i, config)); let mut longest_context_len = 1;
let prefix_context = if config.context {
for item in items {
let context_len = item.security_context.len();
longest_context_len = context_len.max(longest_context_len);
}
Some(longest_context_len)
} else {
None
};
let names = items
.iter()
.filter_map(|i| display_file_name(i, config, prefix_context));
match config.format { match config.format {
Format::Columns => display_grid(names, config.width, Direction::TopToBottom, out), Format::Columns => display_grid(names, config.width, Direction::TopToBottom, out),
@ -1581,15 +1650,13 @@ fn display_grid(
/// longest_link_count_len: usize, /// longest_link_count_len: usize,
/// longest_uname_len: usize, /// longest_uname_len: usize,
/// longest_group_len: usize, /// longest_group_len: usize,
/// longest_context_len: usize,
/// longest_size_len: usize, /// longest_size_len: usize,
/// ``` /// ```
/// that decide the maximum possible character count of each field. /// that decide the maximum possible character count of each field.
fn display_item_long( fn display_item_long(
item: &PathData, item: &PathData,
longest_link_count_len: usize, padding: PaddingCollection,
longest_uname_len: usize,
longest_group_len: usize,
longest_size_len: usize,
config: &Config, config: &Config,
out: &mut BufWriter<Stdout>, out: &mut BufWriter<Stdout>,
) { ) {
@ -1610,16 +1677,23 @@ fn display_item_long(
let _ = write!( let _ = write!(
out, out,
"{} {}", "{}{} {}",
display_permissions(md, true), display_permissions(md, true),
pad_left(display_symlink_count(md), longest_link_count_len), if item.security_context.len() > 1 {
// GNU `ls` uses a "." character to indicate a file with a security context,
// but not other alternate access method.
"."
} else {
""
},
pad_left(&display_symlink_count(md), padding.longest_link_count_len),
); );
if config.long.owner { if config.long.owner {
let _ = write!( let _ = write!(
out, out,
" {}", " {}",
pad_right(display_uname(md, config), longest_uname_len) pad_right(&display_uname(md, config), padding.longest_uname_len)
); );
} }
@ -1627,7 +1701,15 @@ fn display_item_long(
let _ = write!( let _ = write!(
out, out,
" {}", " {}",
pad_right(display_group(md, config), longest_group_len) pad_right(&display_group(md, config), padding.longest_group_len)
);
}
if config.context {
let _ = write!(
out,
" {}",
pad_right(&item.security_context, padding.longest_context_len)
); );
} }
@ -1637,19 +1719,19 @@ fn display_item_long(
let _ = write!( let _ = write!(
out, out,
" {}", " {}",
pad_right(display_uname(md, config), longest_uname_len) pad_right(&display_uname(md, config), padding.longest_uname_len)
); );
} }
let _ = writeln!( let _ = writeln!(
out, out,
" {} {} {}", " {} {} {}",
pad_left(display_size_or_rdev(md, config), longest_size_len), pad_left(&display_size_or_rdev(md, config), padding.longest_size_len),
display_date(md, config), display_date(md, config),
// unwrap is fine because it fails when metadata is not available // unwrap is fine because it fails when metadata is not available
// but we already know that it is because it's checked at the // but we already know that it is because it's checked at the
// start of the function. // start of the function.
display_file_name(item, config).unwrap().contents, display_file_name(item, config, None).unwrap().contents,
); );
} }
@ -1873,21 +1955,22 @@ fn classify_file(path: &PathData) -> Option<char> {
/// * `config.indicator_style` to append specific characters to `name` using [`classify_file`]. /// * `config.indicator_style` to append specific characters to `name` using [`classify_file`].
/// * `config.format` to display symlink targets if `Format::Long`. This function is also /// * `config.format` to display symlink targets if `Format::Long`. This function is also
/// responsible for coloring symlink target names if `config.color` is specified. /// responsible for coloring symlink target names if `config.color` is specified.
/// * `config.context` to prepend security context to `name` if compiled with `feat_selinux`.
/// ///
/// Note that non-unicode sequences in symlink targets are dealt with using /// Note that non-unicode sequences in symlink targets are dealt with using
/// [`std::path::Path::to_string_lossy`]. /// [`std::path::Path::to_string_lossy`].
fn display_file_name(path: &PathData, config: &Config) -> Option<Cell> { fn display_file_name(
path: &PathData,
config: &Config,
prefix_context: Option<usize>,
) -> Option<Cell> {
// This is our return value. We start by `&path.display_name` and modify it along the way. // This is our return value. We start by `&path.display_name` and modify it along the way.
let mut name = escape_name(&path.display_name, &config.quoting_style); let mut name = escape_name(&path.display_name, &config.quoting_style);
#[cfg(unix)] #[cfg(unix)]
{ {
if config.format != Format::Long && config.inode { if config.format != Format::Long && config.inode {
name = path name = path.md().map_or_else(|| "?".to_string(), get_inode) + " " + &name;
.md()
.map_or_else(|| "?".to_string(), |md| get_inode(md))
+ " "
+ &name;
} }
} }
@ -1968,6 +2051,20 @@ fn display_file_name(path: &PathData, config: &Config) -> Option<Cell> {
} }
} }
// Prepend the security context to the `name` and adjust `width` in order
// to get correct alignment from later calls to`display_grid()`.
if config.context {
if let Some(pad_count) = prefix_context {
let security_context = if !matches!(config.format, Format::Commas) {
pad_left(&path.security_context, pad_count)
} else {
path.security_context.to_owned()
};
name = format!("{} {}", security_context, name);
width += security_context.len() + 1;
}
}
Some(Cell { Some(Cell {
contents: name, contents: name,
width, width,
@ -1992,3 +2089,42 @@ fn display_symlink_count(_metadata: &Metadata) -> String {
fn display_symlink_count(metadata: &Metadata) -> String { fn display_symlink_count(metadata: &Metadata) -> String {
metadata.nlink().to_string() metadata.nlink().to_string()
} }
// This returns the SELinux security context as UTF8 `String`.
// In the long term this should be changed to `OsStr`, see discussions at #2621/#2656
#[allow(unused_variables)]
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
let substitute_string = "?".to_string();
if config.selinux_supported {
#[cfg(feature = "selinux")]
{
match selinux::SecurityContext::of_path(p_buf, must_dereference, false) {
Err(_r) => {
// TODO: show the actual reason why it failed
show_warning!("failed to get security context of: {}", p_buf.quote());
substitute_string
}
Ok(None) => substitute_string,
Ok(Some(context)) => {
let context = context.as_bytes();
let context = context.strip_suffix(&[0]).unwrap_or(context);
String::from_utf8(context.to_vec()).unwrap_or_else(|e| {
show_warning!(
"getting security context of: {}: {}",
p_buf.quote(),
e.to_string()
);
String::from_utf8_lossy(context).into_owned()
})
}
}
}
#[cfg(not(feature = "selinux"))]
{
substitute_string
}
} else {
substitute_string
}
}

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_mkdir" name = "uu_mkdir"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "mkdir ~ (uutils) create DIRECTORY" description = "mkdir ~ (uutils) create DIRECTORY"
@ -17,8 +17,8 @@ path = "src/mkdir.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "mode"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "mkdir" name = "mkdir"

View file

@ -5,15 +5,22 @@
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
// spell-checker:ignore (ToDO) ugoa cmode
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use clap::OsValues; use clap::OsValues;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg, ArgMatches};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError}; use uucore::error::{FromIo, UResult, USimpleError};
#[cfg(not(windows))]
use uucore::mode;
use uucore::InvalidEncodingHandling;
static DEFAULT_PERM: u32 = 0o755;
static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist"; static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist";
mod options { mod options {
@ -26,29 +33,81 @@ mod options {
fn usage() -> String { fn usage() -> String {
format!("{0} [OPTION]... [USER]", uucore::execution_phrase()) format!("{0} [OPTION]... [USER]", uucore::execution_phrase())
} }
fn get_long_usage() -> String {
String::from("Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.")
}
#[cfg(windows)]
fn get_mode(_matches: &ArgMatches, _mode_had_minus_prefix: bool) -> Result<u32, String> {
Ok(DEFAULT_PERM)
}
#[cfg(not(windows))]
fn get_mode(matches: &ArgMatches, mode_had_minus_prefix: bool) -> Result<u32, String> {
let digits: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
// Translate a ~str in octal form to u16, default to 755
// Not tested on Windows
let mut new_mode = DEFAULT_PERM;
match matches.value_of(options::MODE) {
Some(m) => {
for mode in m.split(',') {
if mode.contains(digits) {
new_mode = mode::parse_numeric(new_mode, m, true)?;
} else {
let cmode = if mode_had_minus_prefix {
// clap parsing is finished, now put prefix back
format!("-{}", mode)
} else {
mode.to_string()
};
new_mode = mode::parse_symbolic(new_mode, &cmode, mode::get_umask(), true)?;
}
}
Ok(new_mode)
}
None => Ok(DEFAULT_PERM),
}
}
#[cfg(windows)]
fn strip_minus_from_mode(_args: &mut Vec<String>) -> bool {
false
}
#[cfg(not(windows))]
fn strip_minus_from_mode(args: &mut Vec<String>) -> bool {
mode::strip_minus_from_mode(args)
}
#[uucore_procs::gen_uumain] #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let mut args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
// Before we can parse 'args' with clap (and previously getopts),
// a possible MODE prefix '-' needs to be removed (e.g. "chmod -x FILE").
let mode_had_minus_prefix = strip_minus_from_mode(&mut args);
let usage = usage(); let usage = usage();
let after_help = get_long_usage();
// Linux-specific options, not implemented // Linux-specific options, not implemented
// opts.optflag("Z", "context", "set SELinux security context" + // opts.optflag("Z", "context", "set SELinux security context" +
// " of each created directory to CTX"), // " of each created directory to CTX"),
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app()
.usage(&usage[..])
.after_help(&after_help[..])
.get_matches_from(args);
let dirs = matches.values_of_os(options::DIRS).unwrap_or_default(); let dirs = matches.values_of_os(options::DIRS).unwrap_or_default();
let verbose = matches.is_present(options::VERBOSE); let verbose = matches.is_present(options::VERBOSE);
let recursive = matches.is_present(options::PARENTS); let recursive = matches.is_present(options::PARENTS);
// Translate a ~str in octal form to u16, default to 755 match get_mode(&matches, mode_had_minus_prefix) {
// Not tested on Windows Ok(mode) => exec(dirs, recursive, mode, verbose),
let mode: u16 = match matches.value_of(options::MODE) { Err(f) => Err(USimpleError::new(1, f)),
Some(m) => u16::from_str_radix(m, 8) }
.map_err(|_| USimpleError::new(1, format!("invalid mode {}", m.quote())))?,
None => 0o755_u16,
};
exec(dirs, recursive, mode, verbose)
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
@ -86,7 +145,7 @@ pub fn uu_app() -> App<'static, 'static> {
/** /**
* Create the list of new directories * Create the list of new directories
*/ */
fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<()> { fn exec(dirs: OsValues, recursive: bool, mode: u32, verbose: bool) -> UResult<()> {
for dir in dirs { for dir in dirs {
let path = Path::new(dir); let path = Path::new(dir);
show_if_err!(mkdir(path, recursive, mode, verbose)); show_if_err!(mkdir(path, recursive, mode, verbose));
@ -94,7 +153,7 @@ fn exec(dirs: OsValues, recursive: bool, mode: u16, verbose: bool) -> UResult<()
Ok(()) Ok(())
} }
fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()> { fn mkdir(path: &Path, recursive: bool, mode: u32, verbose: bool) -> UResult<()> {
let create_dir = if recursive { let create_dir = if recursive {
fs::create_dir_all fs::create_dir_all
} else { } else {
@ -115,18 +174,18 @@ fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()>
} }
#[cfg(any(unix, target_os = "redox"))] #[cfg(any(unix, target_os = "redox"))]
fn chmod(path: &Path, mode: u16) -> UResult<()> { fn chmod(path: &Path, mode: u32) -> UResult<()> {
use std::fs::{set_permissions, Permissions}; use std::fs::{set_permissions, Permissions};
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
let mode = Permissions::from_mode(u32::from(mode)); let mode = Permissions::from_mode(mode);
set_permissions(path, mode) set_permissions(path, mode)
.map_err_context(|| format!("cannot set permissions {}", path.quote())) .map_err_context(|| format!("cannot set permissions {}", path.quote()))
} }
#[cfg(windows)] #[cfg(windows)]
fn chmod(_path: &Path, _mode: u16) -> UResult<()> { fn chmod(_path: &Path, _mode: u32) -> UResult<()> {
// chmod on Windows only sets the readonly flag, which isn't even honored on directories // chmod on Windows only sets the readonly flag, which isn't even honored on directories
Ok(()) Ok(())
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_mkfifo" name = "uu_mkfifo"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "mkfifo ~ (uutils) create FIFOs (named pipes)" description = "mkfifo ~ (uutils) create FIFOs (named pipes)"
@ -17,9 +17,12 @@ path = "src/mkfifo.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "mkfifo" name = "mkfifo"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_mknod" name = "uu_mknod"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "mknod ~ (uutils) create special file NAME of TYPE" description = "mknod ~ (uutils) create special file NAME of TYPE"
@ -18,9 +18,12 @@ path = "src/mknod.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "^0.2.42" libc = "^0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["mode"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["mode"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "mknod" name = "mknod"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_mktemp" name = "uu_mktemp"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE" description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE"
@ -18,8 +18,8 @@ path = "src/mktemp.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
rand = "0.5" rand = "0.5"
tempfile = "3.1" tempfile = "3.1"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "mktemp" name = "mktemp"

View file

@ -8,9 +8,6 @@
// spell-checker:ignore (paths) GPGHome // spell-checker:ignore (paths) GPGHome
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use uucore::display::{println_verbatim, Quotable}; use uucore::display::{println_verbatim, Quotable};
use uucore::error::{FromIo, UError, UResult}; use uucore::error::{FromIo, UError, UResult};

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_more" name = "uu_more"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "more ~ (uutils) input perusal filter" description = "more ~ (uutils) input perusal filter"
@ -17,7 +17,7 @@ path = "src/more.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" } uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" }
uucore_procs = { version=">=0.0.6", package = "uucore_procs", path = "../../uucore_procs" } uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" }
crossterm = ">=0.19" crossterm = ">=0.19"
atty = "0.2" atty = "0.2"
unicode-width = "0.1.7" unicode-width = "0.1.7"
@ -28,12 +28,11 @@ redox_termios = "0.1"
redox_syscall = "0.2" redox_syscall = "0.2"
[target.'cfg(all(unix, not(target_os = "fuchsia")))'.dependencies] [target.'cfg(all(unix, not(target_os = "fuchsia")))'.dependencies]
nix = "0.19" nix = "=0.23.1"
[[bin]] [[bin]]
name = "more" name = "more"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -210,7 +210,7 @@ fn reset_term(stdout: &mut std::io::Stdout) {
#[inline(always)] #[inline(always)]
fn reset_term(_: &mut usize) {} fn reset_term(_: &mut usize) {}
fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) { fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
let (cols, rows) = terminal::size().unwrap(); let (cols, rows) = terminal::size().unwrap();
let lines = break_buff(buff, usize::from(cols)); let lines = break_buff(buff, usize::from(cols));
@ -232,7 +232,7 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bo
code: KeyCode::Char('c'), code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL, modifiers: KeyModifiers::CONTROL,
}) => { }) => {
reset_term(&mut stdout); reset_term(stdout);
std::process::exit(0); std::process::exit(0);
} }
Event::Key(KeyEvent { Event::Key(KeyEvent {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_mv" name = "uu_mv"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION" description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION"
@ -17,13 +17,12 @@ path = "src/mv.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
fs_extra = "1.1.0" fs_extra = "1.1.0"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "mv" name = "mv"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_nice" name = "uu_nice"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "nice ~ (uutils) run PROGRAM with modified scheduling priority" description = "nice ~ (uutils) run PROGRAM with modified scheduling priority"
@ -17,10 +17,13 @@ path = "src/nice.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
nix = "0.20" nix = "=0.23.1"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "nice" name = "nice"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_nl" name = "uu_nl"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "nl ~ (uutils) display input with added line numbers" description = "nl ~ (uutils) display input with added line numbers"
@ -21,13 +21,12 @@ libc = "0.2.42"
memchr = "2.2.0" memchr = "2.2.0"
regex = "1.0.1" regex = "1.0.1"
regex-syntax = "0.6.7" regex-syntax = "0.6.7"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "nl" name = "nl"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_nohup" name = "uu_nohup"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "nohup ~ (uutils) run COMMAND, ignoring hangup signals" description = "nohup ~ (uutils) run COMMAND, ignoring hangup signals"
@ -18,9 +18,12 @@ path = "src/nohup.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
atty = "0.2" atty = "0.2"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "nohup" name = "nohup"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_nproc" name = "uu_nproc"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "nproc ~ (uutils) display the number of processing units available" description = "nproc ~ (uutils) display the number of processing units available"
@ -18,9 +18,12 @@ path = "src/nproc.rs"
libc = "0.2.42" libc = "0.2.42"
num_cpus = "1.10" num_cpus = "1.10"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "nproc" name = "nproc"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_numfmt" name = "uu_numfmt"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "numfmt ~ (uutils) reformat NUMBER" description = "numfmt ~ (uutils) reformat NUMBER"
@ -16,13 +16,12 @@ path = "src/numfmt.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "numfmt" name = "numfmt"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_od" name = "uu_od"
version = "0.0.7" version = "0.0.8"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "od ~ (uutils) display formatted representation of input" description = "od ~ (uutils) display formatted representation of input"
@ -19,13 +19,12 @@ byteorder = "1.3.2"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
half = "1.6" half = "1.6"
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "od" name = "od"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
# Necessary for "make all"
normal = ["uucore_procs"] normal = ["uucore_procs"]

Some files were not shown because too many files have changed in this diff Show more