mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-08-07 08:27:46 +00:00
Merge branch 'main' into printf-rewrite
This commit is contained in:
commit
28810906a3
524 changed files with 9820 additions and 4775 deletions
|
@ -1,2 +1,2 @@
|
||||||
msrv = "1.64.0"
|
msrv = "1.70.0"
|
||||||
cognitive-complexity-threshold = 10
|
cognitive-complexity-threshold = 10
|
||||||
|
|
234
.github/workflows/CICD.yml
vendored
234
.github/workflows/CICD.yml
vendored
|
@ -11,7 +11,7 @@ env:
|
||||||
PROJECT_NAME: coreutils
|
PROJECT_NAME: coreutils
|
||||||
PROJECT_DESC: "Core universal (cross-platform) utilities"
|
PROJECT_DESC: "Core universal (cross-platform) utilities"
|
||||||
PROJECT_AUTH: "uutils"
|
PROJECT_AUTH: "uutils"
|
||||||
RUST_MIN_SRV: "1.64.0"
|
RUST_MIN_SRV: "1.70.0"
|
||||||
# * style job configuration
|
# * 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
|
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
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ jobs:
|
||||||
name: Style/cargo-deny
|
name: Style/cargo-deny
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||||
|
|
||||||
style_deps:
|
style_deps:
|
||||||
|
@ -47,7 +47,7 @@ jobs:
|
||||||
- { os: macos-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 }
|
- { os: windows-latest , features: feat_os_windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
## note: requires 'nightly' toolchain b/c `cargo-udeps` uses the `rustc` '-Z save-analysis' 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>
|
## * ... ref: <https://github.com/est31/cargo-udeps/issues/73>
|
||||||
|
@ -82,192 +82,6 @@ jobs:
|
||||||
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 ; }
|
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
|
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
||||||
|
|
||||||
style_format:
|
|
||||||
name: Style/format
|
|
||||||
runs-on: ${{ matrix.job.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
job:
|
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
components: rustfmt
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Initialize workflow variables
|
|
||||||
id: vars
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## VARs setup
|
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
|
||||||
# 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='' ;
|
|
||||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
|
||||||
outputs CARGO_FEATURES_OPTION
|
|
||||||
- name: "`cargo fmt` testing"
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## `cargo fmt` testing
|
|
||||||
unset fault
|
|
||||||
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
|
||||||
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
|
|
||||||
|
|
||||||
fuzz:
|
|
||||||
name: Run the fuzzers
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
RUN_FOR: 60
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
|
||||||
- name: Install `cargo-fuzz`
|
|
||||||
run: cargo install cargo-fuzz
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Run fuzz_date for XX seconds
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## Run it
|
|
||||||
cd fuzz
|
|
||||||
cargo +nightly fuzz run fuzz_date -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
|
||||||
- name: Run fuzz_parse_glob for XX seconds
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## Run it
|
|
||||||
cd fuzz
|
|
||||||
cargo +nightly fuzz run fuzz_parse_glob -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
|
||||||
- name: Run fuzz_parse_size for XX seconds
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## Run it
|
|
||||||
cd fuzz
|
|
||||||
cargo +nightly fuzz run fuzz_parse_size -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
|
||||||
- name: Run fuzz_parse_time for XX seconds
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## Run it
|
|
||||||
cd fuzz
|
|
||||||
cargo +nightly fuzz run fuzz_parse_time -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
|
||||||
|
|
||||||
style_lint:
|
|
||||||
name: Style/lint
|
|
||||||
runs-on: ${{ matrix.job.os }}
|
|
||||||
env:
|
|
||||||
SCCACHE_GHA_ENABLED: "true"
|
|
||||||
RUSTC_WRAPPER: "sccache"
|
|
||||||
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@v3
|
|
||||||
- uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
components: clippy
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Run sccache-cache
|
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
|
||||||
- name: Initialize workflow variables
|
|
||||||
id: vars
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## VARs setup
|
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
|
||||||
# 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 -n "-puu_${u} "; done;)"
|
|
||||||
outputs CARGO_UTILITY_LIST_OPTIONS
|
|
||||||
- name: Install/setup prerequisites
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## Install/setup prerequisites
|
|
||||||
case '${{ matrix.job.os }}' in
|
|
||||||
macos-latest) brew install coreutils ;; # needed for show-utils.sh
|
|
||||||
esac
|
|
||||||
- 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_UTILITY_LIST_OPTIONS }} -- -W clippy::default_trait_access -W clippy::manual_string_new -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
|
|
||||||
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
|
||||||
|
|
||||||
style_spellcheck:
|
|
||||||
name: Style/spelling
|
|
||||||
runs-on: ${{ matrix.job.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
job:
|
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Initialize workflow variables
|
|
||||||
id: vars
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
## VARs setup
|
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
|
||||||
# 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
|
|
||||||
|
|
||||||
doc_warnings:
|
doc_warnings:
|
||||||
name: Documentation/warnings
|
name: Documentation/warnings
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
@ -285,7 +99,7 @@ jobs:
|
||||||
# - { os: macos-latest , features: feat_os_macos }
|
# - { os: macos-latest , features: feat_os_macos }
|
||||||
# - { os: windows-latest , features: feat_os_windows }
|
# - { os: windows-latest , features: feat_os_windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
@ -319,9 +133,9 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
||||||
- uses: DavidAnson/markdownlint-cli2-action@v11
|
- uses: DavidAnson/markdownlint-cli2-action@v13
|
||||||
with:
|
with:
|
||||||
command: fix
|
fix: "true"
|
||||||
globs: |
|
globs: |
|
||||||
*.md
|
*.md
|
||||||
docs/src/*.md
|
docs/src/*.md
|
||||||
|
@ -338,7 +152,7 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||||
|
@ -406,7 +220,7 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: "`cargo update` testing"
|
- name: "`cargo update` testing"
|
||||||
|
@ -429,7 +243,7 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: taiki-e/install-action@nextest
|
- uses: taiki-e/install-action@nextest
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
@ -483,7 +297,7 @@ jobs:
|
||||||
- { os: macos-latest , features: feat_os_macos }
|
- { os: macos-latest , features: feat_os_macos }
|
||||||
- { os: windows-latest , features: feat_os_windows }
|
- { os: windows-latest , features: feat_os_windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: taiki-e/install-action@nextest
|
- uses: taiki-e/install-action@nextest
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
@ -510,7 +324,7 @@ jobs:
|
||||||
- { os: macos-latest , features: feat_os_macos }
|
- { os: macos-latest , features: feat_os_macos }
|
||||||
- { os: windows-latest , features: feat_os_windows }
|
- { os: windows-latest , features: feat_os_windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
- uses: taiki-e/install-action@nextest
|
- uses: taiki-e/install-action@nextest
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
@ -534,7 +348,7 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
|
@ -596,6 +410,12 @@ jobs:
|
||||||
check() {
|
check() {
|
||||||
# Warn if the size increases by more than 5%
|
# Warn if the size increases by more than 5%
|
||||||
threshold='1.05'
|
threshold='1.05'
|
||||||
|
|
||||||
|
if [[ "$2" -eq 0 || "$3" -eq 0 ]]; then
|
||||||
|
echo "::warning file=$4::Invalid size for $1. Sizes cannot be 0."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
ratio=$(jq -n "$2 / $3")
|
ratio=$(jq -n "$2 / $3")
|
||||||
echo "$1: size=$2, previous_size=$3, ratio=$ratio, threshold=$threshold"
|
echo "$1: size=$2, previous_size=$3, ratio=$ratio, threshold=$threshold"
|
||||||
if [[ "$(jq -n "$ratio > $threshold")" == 'true' ]]; then
|
if [[ "$(jq -n "$ratio > $threshold")" == 'true' ]]; then
|
||||||
|
@ -654,7 +474,7 @@ jobs:
|
||||||
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly
|
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly
|
||||||
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||||
|
@ -913,7 +733,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
## VARs setup
|
## VARs setup
|
||||||
echo "TEST_SUMMARY_FILE=busybox-result.json" >> $GITHUB_OUTPUT
|
echo "TEST_SUMMARY_FILE=busybox-result.json" >> $GITHUB_OUTPUT
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
|
@ -993,7 +813,7 @@ jobs:
|
||||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||||
TEST_SUMMARY_FILE="toybox-result.json"
|
TEST_SUMMARY_FILE="toybox-result.json"
|
||||||
outputs TEST_SUMMARY_FILE
|
outputs TEST_SUMMARY_FILE
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||||
|
@ -1060,15 +880,6 @@ jobs:
|
||||||
name: toybox-result.json
|
name: toybox-result.json
|
||||||
path: ${{ steps.vars.outputs.TEST_SUMMARY_FILE }}
|
path: ${{ steps.vars.outputs.TEST_SUMMARY_FILE }}
|
||||||
|
|
||||||
toml_format:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Clone repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Check
|
|
||||||
run: npx --yes @taplo/cli fmt --check
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
name: Code Coverage
|
name: Code Coverage
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
@ -1084,7 +895,7 @@ jobs:
|
||||||
- { os: macos-latest , features: macos, toolchain: nightly }
|
- { os: macos-latest , features: macos, toolchain: nightly }
|
||||||
- { os: windows-latest , features: windows, toolchain: nightly-x86_64-pc-windows-gnu }
|
- { os: windows-latest , features: windows, toolchain: nightly-x86_64-pc-windows-gnu }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.job.toolchain }}
|
toolchain: ${{ matrix.job.toolchain }}
|
||||||
|
@ -1201,4 +1012,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
|
||||||
|
|
||||||
|
|
26
.github/workflows/CheckScripts.yml
vendored
26
.github/workflows/CheckScripts.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Run ShellCheck
|
- name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
env:
|
env:
|
||||||
|
@ -41,34 +41,16 @@ jobs:
|
||||||
|
|
||||||
shell_fmt:
|
shell_fmt:
|
||||||
name: ShellScript/Format
|
name: ShellScript/Format
|
||||||
# no need to run in pr events
|
|
||||||
# shfmt will be performed on main branch when the PR is merged
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ shell_check ]
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
pull-requests: write
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup shfmt
|
- name: Setup shfmt
|
||||||
uses: mfinelli/setup-shfmt@v2
|
uses: mfinelli/setup-shfmt@v3
|
||||||
- name: Run shfmt
|
- name: Run shfmt
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# show differs first for every files that need to be formatted
|
|
||||||
# fmt options: bash syntax, 4 spaces indent, indent for switch-case
|
# fmt options: bash syntax, 4 spaces indent, indent for switch-case
|
||||||
echo "## show the differences between formatted and original scripts..."
|
echo "## show the differences between formatted and original scripts..."
|
||||||
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -d || true
|
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -d || true
|
||||||
# perform a shell format
|
|
||||||
echo "## perform a shell format..."
|
|
||||||
# ignore the error code because `-d` will always return false when the file has difference
|
|
||||||
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -w
|
|
||||||
- name: Commit any changes
|
|
||||||
uses: EndBug/add-and-commit@v9
|
|
||||||
with:
|
|
||||||
default_author: github_actions
|
|
||||||
message: "style: auto format by CI (shfmt)"
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
|
|
4
.github/workflows/FixPR.yml
vendored
4
.github/workflows/FixPR.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Initialize job variables
|
- name: Initialize job variables
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -85,7 +85,7 @@ jobs:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-latest , features: feat_os_unix }
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Initialize job variables
|
- name: Initialize job variables
|
||||||
id: vars
|
id: vars
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
12
.github/workflows/GnuTests.yml
vendored
12
.github/workflows/GnuTests.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
||||||
outputs path_GNU path_GNU_tests path_reference path_UUTILS
|
outputs path_GNU path_GNU_tests path_reference path_UUTILS
|
||||||
#
|
#
|
||||||
repo_default_branch="${{ github.event.repository.default_branch }}"
|
repo_default_branch="${{ github.event.repository.default_branch }}"
|
||||||
repo_GNU_ref="v9.3"
|
repo_GNU_ref="v9.4"
|
||||||
repo_reference_branch="${{ github.event.repository.default_branch }}"
|
repo_reference_branch="${{ github.event.repository.default_branch }}"
|
||||||
outputs repo_default_branch repo_GNU_ref repo_reference_branch
|
outputs repo_default_branch repo_GNU_ref repo_reference_branch
|
||||||
#
|
#
|
||||||
|
@ -55,7 +55,7 @@ jobs:
|
||||||
TEST_FULL_SUMMARY_FILE='gnu-full-result.json'
|
TEST_FULL_SUMMARY_FILE='gnu-full-result.json'
|
||||||
outputs SUITE_LOG_FILE ROOT_SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE TEST_FULL_SUMMARY_FILE
|
outputs SUITE_LOG_FILE ROOT_SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE TEST_FULL_SUMMARY_FILE
|
||||||
- name: Checkout code (uutil)
|
- name: Checkout code (uutil)
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: '${{ steps.vars.outputs.path_UUTILS }}'
|
path: '${{ steps.vars.outputs.path_UUTILS }}'
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
@ -66,7 +66,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
workspaces: "./${{ steps.vars.outputs.path_UUTILS }} -> target"
|
workspaces: "./${{ steps.vars.outputs.path_UUTILS }} -> target"
|
||||||
- name: Checkout code (GNU coreutils)
|
- name: Checkout code (GNU coreutils)
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'coreutils/coreutils'
|
repository: 'coreutils/coreutils'
|
||||||
path: '${{ steps.vars.outputs.path_GNU }}'
|
path: '${{ steps.vars.outputs.path_GNU }}'
|
||||||
|
@ -307,15 +307,15 @@ jobs:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code uutil
|
- name: Checkout code uutil
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: 'uutils'
|
path: 'uutils'
|
||||||
- name: Checkout GNU coreutils
|
- name: Checkout GNU coreutils
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'coreutils/coreutils'
|
repository: 'coreutils/coreutils'
|
||||||
path: 'gnu'
|
path: 'gnu'
|
||||||
ref: 'v9.3'
|
ref: 'v9.4'
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- uses: dtolnay/rust-toolchain@master
|
- uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
|
|
2
.github/workflows/android.yml
vendored
2
.github/workflows/android.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
TERMUX: v0.118.0
|
TERMUX: v0.118.0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Restore AVD cache
|
- name: Restore AVD cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
id: avd-cache
|
id: avd-cache
|
||||||
|
|
180
.github/workflows/code-quality.yml
vendored
Normal file
180
.github/workflows/code-quality.yml
vendored
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
name: Code Quality
|
||||||
|
|
||||||
|
# spell-checker:ignore TERMUX reactivecircus Swatinem noaudio pkill swiftshader dtolnay juliangruber
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# * 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
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
# End the current execution if there is a new changeset in the PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
style_format:
|
||||||
|
name: Style/format
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
components: rustfmt
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
- name: Initialize workflow variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||||
|
# 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='' ;
|
||||||
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||||
|
outputs CARGO_FEATURES_OPTION
|
||||||
|
- name: "`cargo fmt` testing"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## `cargo fmt` testing
|
||||||
|
unset fault
|
||||||
|
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
||||||
|
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
|
||||||
|
|
||||||
|
style_lint:
|
||||||
|
name: Style/lint
|
||||||
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
env:
|
||||||
|
SCCACHE_GHA_ENABLED: "true"
|
||||||
|
RUSTC_WRAPPER: "sccache"
|
||||||
|
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@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
components: clippy
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
- name: Run sccache-cache
|
||||||
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
|
- name: Initialize workflow variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||||
|
# 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 -n "-puu_${u} "; done;)"
|
||||||
|
outputs CARGO_UTILITY_LIST_OPTIONS
|
||||||
|
- name: Install/setup prerequisites
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## Install/setup prerequisites
|
||||||
|
case '${{ matrix.job.os }}' in
|
||||||
|
macos-latest) brew install coreutils ;; # needed for show-utils.sh
|
||||||
|
esac
|
||||||
|
- name: "`cargo clippy` lint testing"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## `cargo clippy` lint testing
|
||||||
|
unset fault
|
||||||
|
CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new -W clippy::cognitive_complexity"
|
||||||
|
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_UTILITY_LIST_OPTIONS }} -- ${CLIPPY_FLAGS} -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 }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
job:
|
||||||
|
- { os: ubuntu-latest , features: feat_os_unix }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Initialize workflow variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
## VARs setup
|
||||||
|
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||||
|
# 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
|
||||||
|
|
||||||
|
toml_format:
|
||||||
|
name: Style/toml
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check
|
||||||
|
run: npx --yes @taplo/cli fmt --check
|
4
.github/workflows/freebsd.yml
vendored
4
.github/workflows/freebsd.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
||||||
SCCACHE_GHA_ENABLED: "true"
|
SCCACHE_GHA_ENABLED: "true"
|
||||||
RUSTC_WRAPPER: "sccache"
|
RUSTC_WRAPPER: "sccache"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
|
@ -120,7 +120,7 @@ jobs:
|
||||||
SCCACHE_GHA_ENABLED: "true"
|
SCCACHE_GHA_ENABLED: "true"
|
||||||
RUSTC_WRAPPER: "sccache"
|
RUSTC_WRAPPER: "sccache"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.3
|
||||||
|
|
64
.github/workflows/fuzzing.yml
vendored
Normal file
64
.github/workflows/fuzzing.yml
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
name: Fuzzing
|
||||||
|
|
||||||
|
# spell-checker:ignore fuzzer
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
# End the current execution if there is a new changeset in the PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fuzz:
|
||||||
|
name: Run the fuzzers
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
RUN_FOR: 60
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- name: Install `cargo-fuzz`
|
||||||
|
run: cargo install cargo-fuzz
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
- name: Restore Cached Corpus
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
key: corpus-cache
|
||||||
|
path: |
|
||||||
|
fuzz/corpus
|
||||||
|
- name: Run fuzz_date for XX seconds
|
||||||
|
continue-on-error: true
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo +nightly fuzz run fuzz_date -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||||
|
- name: Run fuzz_test for XX seconds
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo +nightly fuzz run fuzz_test -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||||
|
- name: Run fuzz_expr for XX seconds
|
||||||
|
continue-on-error: true
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo +nightly fuzz run fuzz_expr -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||||
|
- name: Run fuzz_parse_glob for XX seconds
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo +nightly fuzz run fuzz_parse_glob -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||||
|
- name: Run fuzz_parse_size for XX seconds
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo +nightly fuzz run fuzz_parse_size -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||||
|
- name: Run fuzz_parse_time for XX seconds
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cargo +nightly fuzz run fuzz_parse_time -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
|
||||||
|
- name: Save Corpus Cache
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
with:
|
||||||
|
key: corpus-cache
|
||||||
|
path: |
|
||||||
|
fuzz/corpus
|
|
@ -58,6 +58,7 @@ MinGW
|
||||||
Minix
|
Minix
|
||||||
NetBSD
|
NetBSD
|
||||||
Novell
|
Novell
|
||||||
|
Nushell
|
||||||
OpenBSD
|
OpenBSD
|
||||||
POSIX
|
POSIX
|
||||||
PowerPC
|
PowerPC
|
||||||
|
|
|
@ -83,6 +83,8 @@ codespell
|
||||||
commitlint
|
commitlint
|
||||||
dprint
|
dprint
|
||||||
dtrace
|
dtrace
|
||||||
|
flamegraph
|
||||||
|
flamegraphs
|
||||||
gcov
|
gcov
|
||||||
gmake
|
gmake
|
||||||
grcov
|
grcov
|
||||||
|
@ -90,6 +92,7 @@ grep
|
||||||
markdownlint
|
markdownlint
|
||||||
rerast
|
rerast
|
||||||
rollup
|
rollup
|
||||||
|
samply
|
||||||
sed
|
sed
|
||||||
selinuxenabled
|
selinuxenabled
|
||||||
sestatus
|
sestatus
|
||||||
|
|
219
CONTRIBUTING.md
219
CONTRIBUTING.md
|
@ -38,199 +38,15 @@ CI. However, you can use `#[cfg(...)]` attributes to create platform dependent f
|
||||||
VirtualBox and Parallels) for development:
|
VirtualBox and Parallels) for development:
|
||||||
<https://developer.microsoft.com/windows/downloads/virtual-machines/>
|
<https://developer.microsoft.com/windows/downloads/virtual-machines/>
|
||||||
|
|
||||||
## Tools
|
## Setting up your development environment
|
||||||
|
|
||||||
We have an extensive CI that will check your code before it can be merged. This
|
To setup your local development environment for this project please follow [DEVELOPMENT.md guide](DEVELOPMENT.md)
|
||||||
section explains how to run those checks locally to avoid waiting for the CI.
|
|
||||||
|
|
||||||
### pre-commit hooks
|
It covers [installation of necessary tools and prerequisites](DEVELOPMENT.md#tools) as well as using those tools to [test your code changes locally](DEVELOPMENT.md#testing)
|
||||||
|
|
||||||
A configuration for `pre-commit` is provided in the repository. It allows
|
## Improving the GNU compatibility
|
||||||
automatically checking every git commit you make to ensure it compiles, and
|
|
||||||
passes `clippy` and `rustfmt` without warnings.
|
|
||||||
|
|
||||||
To use the provided hook:
|
Please make sure you have installed [GNU utils and prerequisites](DEVELOPMENT.md#gnu-utils-and-prerequisites) and can execute commands described in [Comparing with GNU](DEVELOPMENT.md#comparing-with-gnu) section of [DEVELOPMENT.md](DEVELOPMENT.md)
|
||||||
|
|
||||||
1. [Install `pre-commit`](https://pre-commit.com/#install)
|
|
||||||
1. Run `pre-commit install` while in the repository directory
|
|
||||||
|
|
||||||
Your git commits will then automatically be checked. If a check fails, an error
|
|
||||||
message will explain why, and your commit will be canceled. You can then make
|
|
||||||
the suggested changes, and run `git commit ...` again.
|
|
||||||
|
|
||||||
### clippy
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo clippy --all-targets --all-features
|
|
||||||
```
|
|
||||||
|
|
||||||
The `msrv` key in the clippy configuration file `clippy.toml` is used to disable
|
|
||||||
lints pertaining to newer features by specifying the minimum supported Rust
|
|
||||||
version (MSRV).
|
|
||||||
|
|
||||||
### rustfmt
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo fmt --all
|
|
||||||
```
|
|
||||||
|
|
||||||
### cargo-deny
|
|
||||||
|
|
||||||
This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to
|
|
||||||
detect duplicate dependencies, checks licenses, etc. To run it locally, first
|
|
||||||
install it and then run with:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo deny --all-features check all
|
|
||||||
```
|
|
||||||
|
|
||||||
### Markdown linter
|
|
||||||
|
|
||||||
We use [markdownlint](https://github.com/DavidAnson/markdownlint) to lint the
|
|
||||||
Markdown files in the repository.
|
|
||||||
|
|
||||||
### Spell checker
|
|
||||||
|
|
||||||
We use `cspell` as spell checker for all files in the project. If you are using
|
|
||||||
VS Code, you can install the
|
|
||||||
[code spell checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
|
|
||||||
extension to enable spell checking within your editor. Otherwise, you can
|
|
||||||
install [cspell](https://cspell.org/) separately.
|
|
||||||
|
|
||||||
If you want to make the spell checker ignore a word, you can add
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// spell-checker:ignore word_to_ignore
|
|
||||||
```
|
|
||||||
|
|
||||||
at the top of the file.
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
Testing can be done using either Cargo or `make`.
|
|
||||||
|
|
||||||
### Testing with Cargo
|
|
||||||
|
|
||||||
Just like with building, we follow the standard procedure for testing using
|
|
||||||
Cargo:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo test
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, `cargo test` only runs the common programs. To run also platform
|
|
||||||
specific tests, run:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo test --features unix
|
|
||||||
```
|
|
||||||
|
|
||||||
If you would prefer to test a select few utilities:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo test --features "chmod mv tail" --no-default-features
|
|
||||||
```
|
|
||||||
|
|
||||||
If you also want to test the core utilities:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo test -p uucore -p coreutils
|
|
||||||
```
|
|
||||||
|
|
||||||
Running the complete test suite might take a while. We use [nextest](https://nexte.st/index.html) in
|
|
||||||
the CI and you might want to try it out locally. It can speed up the execution time of the whole
|
|
||||||
test run significantly if the cpu has multiple cores.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cargo nextest run --features unix --no-fail-fast
|
|
||||||
```
|
|
||||||
|
|
||||||
To debug:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
gdb --args target/debug/coreutils ls
|
|
||||||
(gdb) b ls.rs:79
|
|
||||||
(gdb) run
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing with GNU Make
|
|
||||||
|
|
||||||
To simply test all available utilities:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make test
|
|
||||||
```
|
|
||||||
|
|
||||||
To test all but a few of the available utilities:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make SKIP_UTILS='UTILITY_1 UTILITY_2' test
|
|
||||||
```
|
|
||||||
|
|
||||||
To test only a few of the available utilities:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make UTILS='UTILITY_1 UTILITY_2' test
|
|
||||||
```
|
|
||||||
|
|
||||||
To include tests for unimplemented behavior:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make UTILS='UTILITY_1 UTILITY_2' SPEC=y test
|
|
||||||
```
|
|
||||||
|
|
||||||
To run tests with `nextest` just use the nextest target. Note you'll need to
|
|
||||||
[install](https://nexte.st/book/installation.html) `nextest` first. The `nextest` target accepts the
|
|
||||||
same arguments like the default `test` target, so it's possible to pass arguments to `nextest run`
|
|
||||||
via `CARGOFLAGS`:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make CARGOFLAGS='--no-fail-fast' UTILS='UTILITY_1 UTILITY_2' nextest
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Busybox Tests
|
|
||||||
|
|
||||||
This testing functionality is only available on *nix operating systems and
|
|
||||||
requires `make`.
|
|
||||||
|
|
||||||
To run busybox tests for all utilities for which busybox has tests
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make busytest
|
|
||||||
```
|
|
||||||
|
|
||||||
To run busybox tests for a few of the available utilities
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make UTILS='UTILITY_1 UTILITY_2' busytest
|
|
||||||
```
|
|
||||||
|
|
||||||
To pass an argument like "-v" to the busybox test runtime
|
|
||||||
|
|
||||||
```shell
|
|
||||||
make UTILS='UTILITY_1 UTILITY_2' RUNTEST_ARGS='-v' busytest
|
|
||||||
```
|
|
||||||
|
|
||||||
### Comparing with GNU
|
|
||||||
|
|
||||||
To run uutils against the GNU test suite locally, run the following commands:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
bash util/build-gnu.sh
|
|
||||||
# Build uutils without release optimizations
|
|
||||||
UU_MAKE_PROFILE=debug bash util/build-gnu.sh
|
|
||||||
bash util/run-gnu-test.sh
|
|
||||||
# To run a single test:
|
|
||||||
bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example
|
|
||||||
# To run several tests:
|
|
||||||
bash util/run-gnu-test.sh tests/touch/not-owner.sh tests/rm/no-give-up.sh # for example
|
|
||||||
# If this is a perl (.pl) test, to run in debug:
|
|
||||||
DEBUG=1 bash util/run-gnu-test.sh tests/misc/sm3sum.pl
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that it relies on individual utilities (not the multicall binary).
|
|
||||||
|
|
||||||
### Improving the GNU compatibility
|
|
||||||
|
|
||||||
The Python script `./util/remaining-gnu-error.py` shows the list of failing
|
The Python script `./util/remaining-gnu-error.py` shows the list of failing
|
||||||
tests in the CI.
|
tests in the CI.
|
||||||
|
@ -320,30 +136,7 @@ gitignore: add temporary files
|
||||||
|
|
||||||
## Code coverage
|
## Code coverage
|
||||||
|
|
||||||
<!-- spell-checker:ignore (flags) Ccodegen Coverflow Cpanic Zinstrument Zpanic -->
|
To generate code coverage report locally please follow [Code coverage report](DEVELOPMENT.md#code-coverage-report) section of [DEVELOPMENT.md](DEVELOPMENT.md)
|
||||||
|
|
||||||
Code coverage report can be generated using [grcov](https://github.com/mozilla/grcov).
|
|
||||||
|
|
||||||
### Using Nightly Rust
|
|
||||||
|
|
||||||
To generate [gcov-based](https://github.com/mozilla/grcov#example-how-to-generate-gcda-files-for-a-rust-project) coverage report
|
|
||||||
|
|
||||||
```shell
|
|
||||||
export CARGO_INCREMENTAL=0
|
|
||||||
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
|
||||||
export RUSTDOCFLAGS="-Cpanic=abort"
|
|
||||||
cargo build <options...> # e.g., --features feat_os_unix
|
|
||||||
cargo test <options...> # e.g., --features feat_os_unix test_pathchk
|
|
||||||
grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --ignore build.rs --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?\#\[derive\()" -o ./target/debug/coverage/
|
|
||||||
# open target/debug/coverage/index.html in browser
|
|
||||||
```
|
|
||||||
|
|
||||||
if changes are not reflected in the report then run `cargo clean` and run the above commands.
|
|
||||||
|
|
||||||
### Using Stable Rust
|
|
||||||
|
|
||||||
If you are using stable version of Rust that doesn't enable code coverage instrumentation by default
|
|
||||||
then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above.
|
|
||||||
|
|
||||||
## Other implementations
|
## Other implementations
|
||||||
|
|
||||||
|
|
746
Cargo.lock
generated
746
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
275
Cargo.toml
275
Cargo.toml
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "coreutils"
|
name = "coreutils"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
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"
|
||||||
|
@ -16,7 +16,7 @@ repository = "https://github.com/uutils/coreutils"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||||
categories = ["command-line-utilities"]
|
categories = ["command-line-utilities"]
|
||||||
rust-version = "1.64.0"
|
rust-version = "1.70.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
@ -100,7 +100,6 @@ feat_common_core = [
|
||||||
"pwd",
|
"pwd",
|
||||||
"readlink",
|
"readlink",
|
||||||
"realpath",
|
"realpath",
|
||||||
"relpath",
|
|
||||||
"rm",
|
"rm",
|
||||||
"rmdir",
|
"rmdir",
|
||||||
"seq",
|
"seq",
|
||||||
|
@ -152,6 +151,7 @@ feat_os_unix = [
|
||||||
"feat_require_crate_cpp",
|
"feat_require_crate_cpp",
|
||||||
"feat_require_unix",
|
"feat_require_unix",
|
||||||
"feat_require_unix_utmpx",
|
"feat_require_unix_utmpx",
|
||||||
|
"feat_require_unix_hostid",
|
||||||
]
|
]
|
||||||
# "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms
|
# "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms
|
||||||
feat_os_windows = [
|
feat_os_windows = [
|
||||||
|
@ -259,87 +259,86 @@ test = ["uu_test"]
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bigdecimal = "0.4"
|
bigdecimal = "0.4"
|
||||||
binary-heap-plus = "0.5.0"
|
binary-heap-plus = "0.5.0"
|
||||||
bstr = "1.6"
|
bstr = "1.7"
|
||||||
bytecount = "0.6.3"
|
bytecount = "0.6.7"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.5.0"
|
||||||
chrono = { version = "^0.4.26", default-features = false, features = [
|
chrono = { version = "^0.4.31", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"alloc",
|
"alloc",
|
||||||
"clock",
|
"clock",
|
||||||
] }
|
] }
|
||||||
clap = { version = "4.3", features = ["wrap_help", "cargo"] }
|
clap = { version = "4.4", features = ["wrap_help", "cargo"] }
|
||||||
clap_complete = "4.3"
|
clap_complete = "4.4"
|
||||||
clap_mangen = "0.2"
|
clap_mangen = "0.2"
|
||||||
compare = "0.1.0"
|
compare = "0.1.0"
|
||||||
coz = { version = "0.1.3" }
|
coz = { version = "0.1.3" }
|
||||||
crossterm = ">=0.26.1"
|
crossterm = ">=0.27.0"
|
||||||
ctrlc = { version = "3.4", features = ["termination"] }
|
ctrlc = { version = "3.4", features = ["termination"] }
|
||||||
exacl = "0.10.0"
|
exacl = "0.11.0"
|
||||||
file_diff = "1.0.0"
|
file_diff = "1.0.0"
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
fs_extra = "1.3.0"
|
fs_extra = "1.3.0"
|
||||||
fts-sys = "0.2"
|
fts-sys = "0.2"
|
||||||
fundu = "1.2.0"
|
fundu = "2.0.0"
|
||||||
gcd = "2.3"
|
gcd = "2.3"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
half = "2.2"
|
half = "2.3"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
is-terminal = "0.4.7"
|
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
libc = "0.2.147"
|
libc = "0.2.149"
|
||||||
lscolors = { version = "0.15.0", default-features = false, features = [
|
lscolors = { version = "0.15.0", default-features = false, features = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
] }
|
] }
|
||||||
memchr = "2"
|
memchr = "2"
|
||||||
memmap2 = "0.7"
|
memmap2 = "0.9"
|
||||||
nix = { version = "0.26", default-features = false }
|
nix = { version = "0.27", default-features = false }
|
||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
|
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
|
||||||
num-bigint = "0.4.3"
|
num-bigint = "0.4.4"
|
||||||
num-traits = "0.2.16"
|
num-traits = "0.2.17"
|
||||||
number_prefix = "0.4"
|
number_prefix = "0.4"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
onig = { version = "~6.4", default-features = false }
|
onig = { version = "~6.4", default-features = false }
|
||||||
parse_datetime = "0.4.0"
|
parse_datetime = "0.5.0"
|
||||||
phf = "0.11.2"
|
phf = "0.11.2"
|
||||||
phf_codegen = "0.11.2"
|
phf_codegen = "0.11.2"
|
||||||
platform-info = "2.0.2"
|
platform-info = "2.0.2"
|
||||||
quick-error = "2.0.1"
|
quick-error = "2.0.1"
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
rayon = "1.7"
|
rayon = "1.8"
|
||||||
redox_syscall = "0.3"
|
redox_syscall = "0.4"
|
||||||
regex = "1.9.1"
|
regex = "1.10.2"
|
||||||
rstest = "0.18.1"
|
rstest = "0.18.2"
|
||||||
rust-ini = "0.19.0"
|
rust-ini = "0.19.0"
|
||||||
same-file = "1.0.6"
|
same-file = "1.0.6"
|
||||||
self_cell = "1.0.1"
|
self_cell = "1.0.1"
|
||||||
selinux = "0.4"
|
selinux = "0.4"
|
||||||
signal-hook = "0.3.17"
|
signal-hook = "0.3.17"
|
||||||
smallvec = { version = "1.11", features = ["union"] }
|
smallvec = { version = "1.11", features = ["union"] }
|
||||||
tempfile = "3.6.0"
|
tempfile = "3.8.1"
|
||||||
term_grid = "0.1.5"
|
uutils_term_grid = "0.3"
|
||||||
terminal_size = "0.2.6"
|
terminal_size = "0.3.0"
|
||||||
textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
time = { version = "0.3" }
|
time = { version = "0.3" }
|
||||||
unicode-segmentation = "1.10.1"
|
unicode-segmentation = "1.10.1"
|
||||||
unicode-width = "0.1.10"
|
unicode-width = "0.1.11"
|
||||||
utf-8 = "0.7.6"
|
utf-8 = "0.7.6"
|
||||||
walkdir = "2.3"
|
walkdir = "2.4"
|
||||||
winapi-util = "0.1.5"
|
winapi-util = "0.1.6"
|
||||||
windows-sys = { version = "0.48.0", default-features = false }
|
windows-sys = { version = "0.48.0", default-features = false }
|
||||||
xattr = "1.0.1"
|
xattr = "1.0.1"
|
||||||
zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
|
zip = { version = "0.6.6", default_features = false, features = ["deflate"] }
|
||||||
|
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
md-5 = "0.10.5"
|
md-5 = "0.10.6"
|
||||||
sha1 = "0.10.5"
|
sha1 = "0.10.6"
|
||||||
sha2 = "0.10.7"
|
sha2 = "0.10.8"
|
||||||
sha3 = "0.10.8"
|
sha3 = "0.10.8"
|
||||||
blake2b_simd = "1.0.1"
|
blake2b_simd = "1.0.2"
|
||||||
blake3 = "1.4.0"
|
blake3 = "1.5.0"
|
||||||
sm3 = "0.4.2"
|
sm3 = "0.4.2"
|
||||||
digest = "0.10.7"
|
digest = "0.10.7"
|
||||||
|
|
||||||
|
@ -362,110 +361,109 @@ zip = { workspace = true, optional = true }
|
||||||
uuhelp_parser = { optional = true, version = ">=0.0.19", path = "src/uuhelp_parser" }
|
uuhelp_parser = { optional = true, version = ">=0.0.19", path = "src/uuhelp_parser" }
|
||||||
|
|
||||||
# * uutils
|
# * uutils
|
||||||
uu_test = { optional = true, version = "0.0.20", package = "uu_test", path = "src/uu/test" }
|
uu_test = { optional = true, version = "0.0.22", package = "uu_test", path = "src/uu/test" }
|
||||||
#
|
#
|
||||||
arch = { optional = true, version = "0.0.20", package = "uu_arch", path = "src/uu/arch" }
|
arch = { optional = true, version = "0.0.22", package = "uu_arch", path = "src/uu/arch" }
|
||||||
base32 = { optional = true, version = "0.0.20", package = "uu_base32", path = "src/uu/base32" }
|
base32 = { optional = true, version = "0.0.22", package = "uu_base32", path = "src/uu/base32" }
|
||||||
base64 = { optional = true, version = "0.0.20", package = "uu_base64", path = "src/uu/base64" }
|
base64 = { optional = true, version = "0.0.22", package = "uu_base64", path = "src/uu/base64" }
|
||||||
basename = { optional = true, version = "0.0.20", package = "uu_basename", path = "src/uu/basename" }
|
basename = { optional = true, version = "0.0.22", package = "uu_basename", path = "src/uu/basename" }
|
||||||
basenc = { optional = true, version = "0.0.20", package = "uu_basenc", path = "src/uu/basenc" }
|
basenc = { optional = true, version = "0.0.22", package = "uu_basenc", path = "src/uu/basenc" }
|
||||||
cat = { optional = true, version = "0.0.20", package = "uu_cat", path = "src/uu/cat" }
|
cat = { optional = true, version = "0.0.22", package = "uu_cat", path = "src/uu/cat" }
|
||||||
chcon = { optional = true, version = "0.0.20", package = "uu_chcon", path = "src/uu/chcon" }
|
chcon = { optional = true, version = "0.0.22", package = "uu_chcon", path = "src/uu/chcon" }
|
||||||
chgrp = { optional = true, version = "0.0.20", package = "uu_chgrp", path = "src/uu/chgrp" }
|
chgrp = { optional = true, version = "0.0.22", package = "uu_chgrp", path = "src/uu/chgrp" }
|
||||||
chmod = { optional = true, version = "0.0.20", package = "uu_chmod", path = "src/uu/chmod" }
|
chmod = { optional = true, version = "0.0.22", package = "uu_chmod", path = "src/uu/chmod" }
|
||||||
chown = { optional = true, version = "0.0.20", package = "uu_chown", path = "src/uu/chown" }
|
chown = { optional = true, version = "0.0.22", package = "uu_chown", path = "src/uu/chown" }
|
||||||
chroot = { optional = true, version = "0.0.20", package = "uu_chroot", path = "src/uu/chroot" }
|
chroot = { optional = true, version = "0.0.22", package = "uu_chroot", path = "src/uu/chroot" }
|
||||||
cksum = { optional = true, version = "0.0.20", package = "uu_cksum", path = "src/uu/cksum" }
|
cksum = { optional = true, version = "0.0.22", package = "uu_cksum", path = "src/uu/cksum" }
|
||||||
comm = { optional = true, version = "0.0.20", package = "uu_comm", path = "src/uu/comm" }
|
comm = { optional = true, version = "0.0.22", package = "uu_comm", path = "src/uu/comm" }
|
||||||
cp = { optional = true, version = "0.0.20", package = "uu_cp", path = "src/uu/cp" }
|
cp = { optional = true, version = "0.0.22", package = "uu_cp", path = "src/uu/cp" }
|
||||||
csplit = { optional = true, version = "0.0.20", package = "uu_csplit", path = "src/uu/csplit" }
|
csplit = { optional = true, version = "0.0.22", package = "uu_csplit", path = "src/uu/csplit" }
|
||||||
cut = { optional = true, version = "0.0.20", package = "uu_cut", path = "src/uu/cut" }
|
cut = { optional = true, version = "0.0.22", package = "uu_cut", path = "src/uu/cut" }
|
||||||
date = { optional = true, version = "0.0.20", package = "uu_date", path = "src/uu/date" }
|
date = { optional = true, version = "0.0.22", package = "uu_date", path = "src/uu/date" }
|
||||||
dd = { optional = true, version = "0.0.20", package = "uu_dd", path = "src/uu/dd" }
|
dd = { optional = true, version = "0.0.22", package = "uu_dd", path = "src/uu/dd" }
|
||||||
df = { optional = true, version = "0.0.20", package = "uu_df", path = "src/uu/df" }
|
df = { optional = true, version = "0.0.22", package = "uu_df", path = "src/uu/df" }
|
||||||
dir = { optional = true, version = "0.0.20", package = "uu_dir", path = "src/uu/dir" }
|
dir = { optional = true, version = "0.0.22", package = "uu_dir", path = "src/uu/dir" }
|
||||||
dircolors = { optional = true, version = "0.0.20", package = "uu_dircolors", path = "src/uu/dircolors" }
|
dircolors = { optional = true, version = "0.0.22", package = "uu_dircolors", path = "src/uu/dircolors" }
|
||||||
dirname = { optional = true, version = "0.0.20", package = "uu_dirname", path = "src/uu/dirname" }
|
dirname = { optional = true, version = "0.0.22", package = "uu_dirname", path = "src/uu/dirname" }
|
||||||
du = { optional = true, version = "0.0.20", package = "uu_du", path = "src/uu/du" }
|
du = { optional = true, version = "0.0.22", package = "uu_du", path = "src/uu/du" }
|
||||||
echo = { optional = true, version = "0.0.20", package = "uu_echo", path = "src/uu/echo" }
|
echo = { optional = true, version = "0.0.22", package = "uu_echo", path = "src/uu/echo" }
|
||||||
env = { optional = true, version = "0.0.20", package = "uu_env", path = "src/uu/env" }
|
env = { optional = true, version = "0.0.22", package = "uu_env", path = "src/uu/env" }
|
||||||
expand = { optional = true, version = "0.0.20", package = "uu_expand", path = "src/uu/expand" }
|
expand = { optional = true, version = "0.0.22", package = "uu_expand", path = "src/uu/expand" }
|
||||||
expr = { optional = true, version = "0.0.20", package = "uu_expr", path = "src/uu/expr" }
|
expr = { optional = true, version = "0.0.22", package = "uu_expr", path = "src/uu/expr" }
|
||||||
factor = { optional = true, version = "0.0.20", package = "uu_factor", path = "src/uu/factor" }
|
factor = { optional = true, version = "0.0.22", package = "uu_factor", path = "src/uu/factor" }
|
||||||
false = { optional = true, version = "0.0.20", package = "uu_false", path = "src/uu/false" }
|
false = { optional = true, version = "0.0.22", package = "uu_false", path = "src/uu/false" }
|
||||||
fmt = { optional = true, version = "0.0.20", package = "uu_fmt", path = "src/uu/fmt" }
|
fmt = { optional = true, version = "0.0.22", package = "uu_fmt", path = "src/uu/fmt" }
|
||||||
fold = { optional = true, version = "0.0.20", package = "uu_fold", path = "src/uu/fold" }
|
fold = { optional = true, version = "0.0.22", package = "uu_fold", path = "src/uu/fold" }
|
||||||
groups = { optional = true, version = "0.0.20", package = "uu_groups", path = "src/uu/groups" }
|
groups = { optional = true, version = "0.0.22", package = "uu_groups", path = "src/uu/groups" }
|
||||||
hashsum = { optional = true, version = "0.0.20", package = "uu_hashsum", path = "src/uu/hashsum" }
|
hashsum = { optional = true, version = "0.0.22", package = "uu_hashsum", path = "src/uu/hashsum" }
|
||||||
head = { optional = true, version = "0.0.20", package = "uu_head", path = "src/uu/head" }
|
head = { optional = true, version = "0.0.22", package = "uu_head", path = "src/uu/head" }
|
||||||
hostid = { optional = true, version = "0.0.20", package = "uu_hostid", path = "src/uu/hostid" }
|
hostid = { optional = true, version = "0.0.22", package = "uu_hostid", path = "src/uu/hostid" }
|
||||||
hostname = { optional = true, version = "0.0.20", package = "uu_hostname", path = "src/uu/hostname" }
|
hostname = { optional = true, version = "0.0.22", package = "uu_hostname", path = "src/uu/hostname" }
|
||||||
id = { optional = true, version = "0.0.20", package = "uu_id", path = "src/uu/id" }
|
id = { optional = true, version = "0.0.22", package = "uu_id", path = "src/uu/id" }
|
||||||
install = { optional = true, version = "0.0.20", package = "uu_install", path = "src/uu/install" }
|
install = { optional = true, version = "0.0.22", package = "uu_install", path = "src/uu/install" }
|
||||||
join = { optional = true, version = "0.0.20", package = "uu_join", path = "src/uu/join" }
|
join = { optional = true, version = "0.0.22", package = "uu_join", path = "src/uu/join" }
|
||||||
kill = { optional = true, version = "0.0.20", package = "uu_kill", path = "src/uu/kill" }
|
kill = { optional = true, version = "0.0.22", package = "uu_kill", path = "src/uu/kill" }
|
||||||
link = { optional = true, version = "0.0.20", package = "uu_link", path = "src/uu/link" }
|
link = { optional = true, version = "0.0.22", package = "uu_link", path = "src/uu/link" }
|
||||||
ln = { optional = true, version = "0.0.20", package = "uu_ln", path = "src/uu/ln" }
|
ln = { optional = true, version = "0.0.22", package = "uu_ln", path = "src/uu/ln" }
|
||||||
ls = { optional = true, version = "0.0.20", package = "uu_ls", path = "src/uu/ls" }
|
ls = { optional = true, version = "0.0.22", package = "uu_ls", path = "src/uu/ls" }
|
||||||
logname = { optional = true, version = "0.0.20", package = "uu_logname", path = "src/uu/logname" }
|
logname = { optional = true, version = "0.0.22", package = "uu_logname", path = "src/uu/logname" }
|
||||||
mkdir = { optional = true, version = "0.0.20", package = "uu_mkdir", path = "src/uu/mkdir" }
|
mkdir = { optional = true, version = "0.0.22", package = "uu_mkdir", path = "src/uu/mkdir" }
|
||||||
mkfifo = { optional = true, version = "0.0.20", package = "uu_mkfifo", path = "src/uu/mkfifo" }
|
mkfifo = { optional = true, version = "0.0.22", package = "uu_mkfifo", path = "src/uu/mkfifo" }
|
||||||
mknod = { optional = true, version = "0.0.20", package = "uu_mknod", path = "src/uu/mknod" }
|
mknod = { optional = true, version = "0.0.22", package = "uu_mknod", path = "src/uu/mknod" }
|
||||||
mktemp = { optional = true, version = "0.0.20", package = "uu_mktemp", path = "src/uu/mktemp" }
|
mktemp = { optional = true, version = "0.0.22", package = "uu_mktemp", path = "src/uu/mktemp" }
|
||||||
more = { optional = true, version = "0.0.20", package = "uu_more", path = "src/uu/more" }
|
more = { optional = true, version = "0.0.22", package = "uu_more", path = "src/uu/more" }
|
||||||
mv = { optional = true, version = "0.0.20", package = "uu_mv", path = "src/uu/mv" }
|
mv = { optional = true, version = "0.0.22", package = "uu_mv", path = "src/uu/mv" }
|
||||||
nice = { optional = true, version = "0.0.20", package = "uu_nice", path = "src/uu/nice" }
|
nice = { optional = true, version = "0.0.22", package = "uu_nice", path = "src/uu/nice" }
|
||||||
nl = { optional = true, version = "0.0.20", package = "uu_nl", path = "src/uu/nl" }
|
nl = { optional = true, version = "0.0.22", package = "uu_nl", path = "src/uu/nl" }
|
||||||
nohup = { optional = true, version = "0.0.20", package = "uu_nohup", path = "src/uu/nohup" }
|
nohup = { optional = true, version = "0.0.22", package = "uu_nohup", path = "src/uu/nohup" }
|
||||||
nproc = { optional = true, version = "0.0.20", package = "uu_nproc", path = "src/uu/nproc" }
|
nproc = { optional = true, version = "0.0.22", package = "uu_nproc", path = "src/uu/nproc" }
|
||||||
numfmt = { optional = true, version = "0.0.20", package = "uu_numfmt", path = "src/uu/numfmt" }
|
numfmt = { optional = true, version = "0.0.22", package = "uu_numfmt", path = "src/uu/numfmt" }
|
||||||
od = { optional = true, version = "0.0.20", package = "uu_od", path = "src/uu/od" }
|
od = { optional = true, version = "0.0.22", package = "uu_od", path = "src/uu/od" }
|
||||||
paste = { optional = true, version = "0.0.20", package = "uu_paste", path = "src/uu/paste" }
|
paste = { optional = true, version = "0.0.22", package = "uu_paste", path = "src/uu/paste" }
|
||||||
pathchk = { optional = true, version = "0.0.20", package = "uu_pathchk", path = "src/uu/pathchk" }
|
pathchk = { optional = true, version = "0.0.22", package = "uu_pathchk", path = "src/uu/pathchk" }
|
||||||
pinky = { optional = true, version = "0.0.20", package = "uu_pinky", path = "src/uu/pinky" }
|
pinky = { optional = true, version = "0.0.22", package = "uu_pinky", path = "src/uu/pinky" }
|
||||||
pr = { optional = true, version = "0.0.20", package = "uu_pr", path = "src/uu/pr" }
|
pr = { optional = true, version = "0.0.22", package = "uu_pr", path = "src/uu/pr" }
|
||||||
printenv = { optional = true, version = "0.0.20", package = "uu_printenv", path = "src/uu/printenv" }
|
printenv = { optional = true, version = "0.0.22", package = "uu_printenv", path = "src/uu/printenv" }
|
||||||
printf = { optional = true, version = "0.0.20", package = "uu_printf", path = "src/uu/printf" }
|
printf = { optional = true, version = "0.0.22", package = "uu_printf", path = "src/uu/printf" }
|
||||||
ptx = { optional = true, version = "0.0.20", package = "uu_ptx", path = "src/uu/ptx" }
|
ptx = { optional = true, version = "0.0.22", package = "uu_ptx", path = "src/uu/ptx" }
|
||||||
pwd = { optional = true, version = "0.0.20", package = "uu_pwd", path = "src/uu/pwd" }
|
pwd = { optional = true, version = "0.0.22", package = "uu_pwd", path = "src/uu/pwd" }
|
||||||
readlink = { optional = true, version = "0.0.20", package = "uu_readlink", path = "src/uu/readlink" }
|
readlink = { optional = true, version = "0.0.22", package = "uu_readlink", path = "src/uu/readlink" }
|
||||||
realpath = { optional = true, version = "0.0.20", package = "uu_realpath", path = "src/uu/realpath" }
|
realpath = { optional = true, version = "0.0.22", package = "uu_realpath", path = "src/uu/realpath" }
|
||||||
relpath = { optional = true, version = "0.0.20", package = "uu_relpath", path = "src/uu/relpath" }
|
rm = { optional = true, version = "0.0.22", package = "uu_rm", path = "src/uu/rm" }
|
||||||
rm = { optional = true, version = "0.0.20", package = "uu_rm", path = "src/uu/rm" }
|
rmdir = { optional = true, version = "0.0.22", package = "uu_rmdir", path = "src/uu/rmdir" }
|
||||||
rmdir = { optional = true, version = "0.0.20", package = "uu_rmdir", path = "src/uu/rmdir" }
|
runcon = { optional = true, version = "0.0.22", package = "uu_runcon", path = "src/uu/runcon" }
|
||||||
runcon = { optional = true, version = "0.0.20", package = "uu_runcon", path = "src/uu/runcon" }
|
seq = { optional = true, version = "0.0.22", package = "uu_seq", path = "src/uu/seq" }
|
||||||
seq = { optional = true, version = "0.0.20", package = "uu_seq", path = "src/uu/seq" }
|
shred = { optional = true, version = "0.0.22", package = "uu_shred", path = "src/uu/shred" }
|
||||||
shred = { optional = true, version = "0.0.20", package = "uu_shred", path = "src/uu/shred" }
|
shuf = { optional = true, version = "0.0.22", package = "uu_shuf", path = "src/uu/shuf" }
|
||||||
shuf = { optional = true, version = "0.0.20", package = "uu_shuf", path = "src/uu/shuf" }
|
sleep = { optional = true, version = "0.0.22", package = "uu_sleep", path = "src/uu/sleep" }
|
||||||
sleep = { optional = true, version = "0.0.20", package = "uu_sleep", path = "src/uu/sleep" }
|
sort = { optional = true, version = "0.0.22", package = "uu_sort", path = "src/uu/sort" }
|
||||||
sort = { optional = true, version = "0.0.20", package = "uu_sort", path = "src/uu/sort" }
|
split = { optional = true, version = "0.0.22", package = "uu_split", path = "src/uu/split" }
|
||||||
split = { optional = true, version = "0.0.20", package = "uu_split", path = "src/uu/split" }
|
stat = { optional = true, version = "0.0.22", package = "uu_stat", path = "src/uu/stat" }
|
||||||
stat = { optional = true, version = "0.0.20", package = "uu_stat", path = "src/uu/stat" }
|
stdbuf = { optional = true, version = "0.0.22", package = "uu_stdbuf", path = "src/uu/stdbuf" }
|
||||||
stdbuf = { optional = true, version = "0.0.20", package = "uu_stdbuf", path = "src/uu/stdbuf" }
|
stty = { optional = true, version = "0.0.22", package = "uu_stty", path = "src/uu/stty" }
|
||||||
stty = { optional = true, version = "0.0.20", package = "uu_stty", path = "src/uu/stty" }
|
sum = { optional = true, version = "0.0.22", package = "uu_sum", path = "src/uu/sum" }
|
||||||
sum = { optional = true, version = "0.0.20", package = "uu_sum", path = "src/uu/sum" }
|
sync = { optional = true, version = "0.0.22", package = "uu_sync", path = "src/uu/sync" }
|
||||||
sync = { optional = true, version = "0.0.20", package = "uu_sync", path = "src/uu/sync" }
|
tac = { optional = true, version = "0.0.22", package = "uu_tac", path = "src/uu/tac" }
|
||||||
tac = { optional = true, version = "0.0.20", package = "uu_tac", path = "src/uu/tac" }
|
tail = { optional = true, version = "0.0.22", package = "uu_tail", path = "src/uu/tail" }
|
||||||
tail = { optional = true, version = "0.0.20", package = "uu_tail", path = "src/uu/tail" }
|
tee = { optional = true, version = "0.0.22", package = "uu_tee", path = "src/uu/tee" }
|
||||||
tee = { optional = true, version = "0.0.20", package = "uu_tee", path = "src/uu/tee" }
|
timeout = { optional = true, version = "0.0.22", package = "uu_timeout", path = "src/uu/timeout" }
|
||||||
timeout = { optional = true, version = "0.0.20", package = "uu_timeout", path = "src/uu/timeout" }
|
touch = { optional = true, version = "0.0.22", package = "uu_touch", path = "src/uu/touch" }
|
||||||
touch = { optional = true, version = "0.0.20", package = "uu_touch", path = "src/uu/touch" }
|
tr = { optional = true, version = "0.0.22", package = "uu_tr", path = "src/uu/tr" }
|
||||||
tr = { optional = true, version = "0.0.20", package = "uu_tr", path = "src/uu/tr" }
|
true = { optional = true, version = "0.0.22", package = "uu_true", path = "src/uu/true" }
|
||||||
true = { optional = true, version = "0.0.20", package = "uu_true", path = "src/uu/true" }
|
truncate = { optional = true, version = "0.0.22", package = "uu_truncate", path = "src/uu/truncate" }
|
||||||
truncate = { optional = true, version = "0.0.20", package = "uu_truncate", path = "src/uu/truncate" }
|
tsort = { optional = true, version = "0.0.22", package = "uu_tsort", path = "src/uu/tsort" }
|
||||||
tsort = { optional = true, version = "0.0.20", package = "uu_tsort", path = "src/uu/tsort" }
|
tty = { optional = true, version = "0.0.22", package = "uu_tty", path = "src/uu/tty" }
|
||||||
tty = { optional = true, version = "0.0.20", package = "uu_tty", path = "src/uu/tty" }
|
uname = { optional = true, version = "0.0.22", package = "uu_uname", path = "src/uu/uname" }
|
||||||
uname = { optional = true, version = "0.0.20", package = "uu_uname", path = "src/uu/uname" }
|
unexpand = { optional = true, version = "0.0.22", package = "uu_unexpand", path = "src/uu/unexpand" }
|
||||||
unexpand = { optional = true, version = "0.0.20", package = "uu_unexpand", path = "src/uu/unexpand" }
|
uniq = { optional = true, version = "0.0.22", package = "uu_uniq", path = "src/uu/uniq" }
|
||||||
uniq = { optional = true, version = "0.0.20", package = "uu_uniq", path = "src/uu/uniq" }
|
unlink = { optional = true, version = "0.0.22", package = "uu_unlink", path = "src/uu/unlink" }
|
||||||
unlink = { optional = true, version = "0.0.20", package = "uu_unlink", path = "src/uu/unlink" }
|
uptime = { optional = true, version = "0.0.22", package = "uu_uptime", path = "src/uu/uptime" }
|
||||||
uptime = { optional = true, version = "0.0.20", package = "uu_uptime", path = "src/uu/uptime" }
|
users = { optional = true, version = "0.0.22", package = "uu_users", path = "src/uu/users" }
|
||||||
users = { optional = true, version = "0.0.20", package = "uu_users", path = "src/uu/users" }
|
vdir = { optional = true, version = "0.0.22", package = "uu_vdir", path = "src/uu/vdir" }
|
||||||
vdir = { optional = true, version = "0.0.20", package = "uu_vdir", path = "src/uu/vdir" }
|
wc = { optional = true, version = "0.0.22", package = "uu_wc", path = "src/uu/wc" }
|
||||||
wc = { optional = true, version = "0.0.20", package = "uu_wc", path = "src/uu/wc" }
|
who = { optional = true, version = "0.0.22", package = "uu_who", path = "src/uu/who" }
|
||||||
who = { optional = true, version = "0.0.20", package = "uu_who", path = "src/uu/who" }
|
whoami = { optional = true, version = "0.0.22", package = "uu_whoami", path = "src/uu/whoami" }
|
||||||
whoami = { optional = true, version = "0.0.20", package = "uu_whoami", path = "src/uu/whoami" }
|
yes = { optional = true, version = "0.0.22", package = "uu_yes", path = "src/uu/yes" }
|
||||||
yes = { optional = true, version = "0.0.20", package = "uu_yes", path = "src/uu/yes" }
|
|
||||||
|
|
||||||
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
|
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
|
||||||
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
|
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
|
||||||
|
@ -490,7 +488,6 @@ time = { workspace = true, features = ["local-offset"] }
|
||||||
unindent = "0.2"
|
unindent = "0.2"
|
||||||
uucore = { workspace = true, features = ["entries", "process", "signals"] }
|
uucore = { workspace = true, features = ["entries", "process", "signals"] }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
is-terminal = { workspace = true }
|
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
rstest = { workspace = true }
|
rstest = { workspace = true }
|
||||||
|
|
||||||
|
|
331
DEVELOPMENT.md
Normal file
331
DEVELOPMENT.md
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
<!-- spell-checker:ignore (flags) Ccodegen Coverflow Cpanic Zinstrument Zpanic reimplementing toybox RUNTEST CARGOFLAGS nextest prereq autopoint gettext texinfo automake findutils shellenv libexec gnubin toolchains -->
|
||||||
|
|
||||||
|
# Setting up your local development environment
|
||||||
|
|
||||||
|
For contributing rules and best practices please refer to [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
For this guide we assume that you already have a GitHub account and have `git` and your favorite code editor or IDE installed and configured.
|
||||||
|
Before you start working on coreutils, please follow these steps:
|
||||||
|
|
||||||
|
1. Fork the [coreutils repository](https://github.com/uutils/coreutils) to your GitHub account.
|
||||||
|
***Tip:*** See [this GitHub guide](https://docs.github.com/en/get-started/quickstart/fork-a-repo) for more information on this step.
|
||||||
|
2. Clone that fork to your local development environment:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/YOUR-GITHUB-ACCOUNT/coreutils
|
||||||
|
cd coreutils
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
You will need the tools mentioned in this section to build and test your code changes locally.
|
||||||
|
This section will explain how to install and configure these tools.
|
||||||
|
We also have an extensive CI that uses these tools and will check your code before it can be merged.
|
||||||
|
The next section [Testing](#testing) will explain how to run those checks locally to avoid waiting for the CI.
|
||||||
|
|
||||||
|
### Rust toolchain
|
||||||
|
|
||||||
|
[Install Rust](https://www.rust-lang.org/tools/install)
|
||||||
|
|
||||||
|
If you're using rustup to install and manage your Rust toolchains, `clippy` and `rustfmt` are usually already installed. If you are using one of the alternative methods, please make sure to install them manually. See following sub-sections for their usage: [clippy](#clippy) [rustfmt](#rustfmt).
|
||||||
|
|
||||||
|
***Tip*** You might also need to add 'llvm-tools' component if you are going to [generate code coverage reports locally](#code-coverage-report):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
rustup component add llvm-tools-preview
|
||||||
|
```
|
||||||
|
|
||||||
|
### GNU utils and prerequisites
|
||||||
|
|
||||||
|
If you are developing on Linux, most likely you already have all/most GNU utilities and prerequisites installed.
|
||||||
|
|
||||||
|
To make sure, please check GNU coreutils [README-prereq](https://github.com/coreutils/coreutils/blob/master/README-prereq).
|
||||||
|
|
||||||
|
You will need these to [run uutils against the GNU test suite locally](#comparing-with-gnu).
|
||||||
|
|
||||||
|
For MacOS and Windows platform specific setup please check [MacOS GNU utils](#macos-gnu-utils) and [Windows GNU utils](#windows-gnu-utils) sections respectfully.
|
||||||
|
|
||||||
|
### pre-commit hooks
|
||||||
|
|
||||||
|
A configuration for `pre-commit` is provided in the repository. It allows
|
||||||
|
automatically checking every git commit you make to ensure it compiles, and
|
||||||
|
passes `clippy` and `rustfmt` without warnings.
|
||||||
|
|
||||||
|
To use the provided hook:
|
||||||
|
|
||||||
|
1. [Install `pre-commit`](https://pre-commit.com/#install)
|
||||||
|
1. Run `pre-commit install` while in the repository directory
|
||||||
|
|
||||||
|
Your git commits will then automatically be checked. If a check fails, an error
|
||||||
|
message will explain why, and your commit will be canceled. You can then make
|
||||||
|
the suggested changes, and run `git commit ...` again.
|
||||||
|
|
||||||
|
**NOTE: On MacOS** the pre-commit hooks are currently broken. There are workarounds involving switching to unstable nightly Rust and components.
|
||||||
|
|
||||||
|
### clippy
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo clippy --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
The `msrv` key in the clippy configuration file `clippy.toml` is used to disable
|
||||||
|
lints pertaining to newer features by specifying the minimum supported Rust
|
||||||
|
version (MSRV).
|
||||||
|
|
||||||
|
### rustfmt
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo fmt --all
|
||||||
|
```
|
||||||
|
|
||||||
|
### cargo-deny
|
||||||
|
|
||||||
|
This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to
|
||||||
|
detect duplicate dependencies, checks licenses, etc. To run it locally, first
|
||||||
|
install it and then run with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo deny --all-features check all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Markdown linter
|
||||||
|
|
||||||
|
We use [markdownlint](https://github.com/DavidAnson/markdownlint) to lint the
|
||||||
|
Markdown files in the repository.
|
||||||
|
|
||||||
|
### Spell checker
|
||||||
|
|
||||||
|
We use `cspell` as spell checker for all files in the project. If you are using
|
||||||
|
VS Code, you can install the
|
||||||
|
[code spell checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
|
||||||
|
extension to enable spell checking within your editor. Otherwise, you can
|
||||||
|
install [cspell](https://cspell.org/) separately.
|
||||||
|
|
||||||
|
If you want to make the spell checker ignore a word, you can add
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// spell-checker:ignore word_to_ignore
|
||||||
|
```
|
||||||
|
|
||||||
|
at the top of the file.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
This section explains how to run our CI checks locally.
|
||||||
|
Testing can be done using either Cargo or `make`.
|
||||||
|
|
||||||
|
### Testing with Cargo
|
||||||
|
|
||||||
|
Just like with building, we follow the standard procedure for testing using
|
||||||
|
Cargo:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, `cargo test` only runs the common programs. To run also platform
|
||||||
|
specific tests, run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo test --features unix
|
||||||
|
```
|
||||||
|
|
||||||
|
If you would prefer to test a select few utilities:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo test --features "chmod mv tail" --no-default-features
|
||||||
|
```
|
||||||
|
|
||||||
|
If you also want to test the core utilities:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo test -p uucore -p coreutils
|
||||||
|
# or
|
||||||
|
cargo test --all-features -p uucore
|
||||||
|
```
|
||||||
|
|
||||||
|
Running the complete test suite might take a while. We use [nextest](https://nexte.st/index.html) in
|
||||||
|
the CI and you might want to try it out locally. It can speed up the execution time of the whole
|
||||||
|
test run significantly if the cpu has multiple cores.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo nextest run --features unix --no-fail-fast
|
||||||
|
```
|
||||||
|
|
||||||
|
To debug:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
rust-gdb --args target/debug/coreutils ls
|
||||||
|
(gdb) b ls.rs:79
|
||||||
|
(gdb) run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing with GNU Make
|
||||||
|
|
||||||
|
To simply test all available utilities:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
To test all but a few of the available utilities:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make SKIP_UTILS='UTILITY_1 UTILITY_2' test
|
||||||
|
```
|
||||||
|
|
||||||
|
To test only a few of the available utilities:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make UTILS='UTILITY_1 UTILITY_2' test
|
||||||
|
```
|
||||||
|
|
||||||
|
To include tests for unimplemented behavior:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make UTILS='UTILITY_1 UTILITY_2' SPEC=y test
|
||||||
|
```
|
||||||
|
|
||||||
|
To run tests with `nextest` just use the nextest target. Note you'll need to
|
||||||
|
[install](https://nexte.st/book/installation.html) `nextest` first. The `nextest` target accepts the
|
||||||
|
same arguments like the default `test` target, so it's possible to pass arguments to `nextest run`
|
||||||
|
via `CARGOFLAGS`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make CARGOFLAGS='--no-fail-fast' UTILS='UTILITY_1 UTILITY_2' nextest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Busybox Tests
|
||||||
|
|
||||||
|
This testing functionality is only available on *nix operating systems and
|
||||||
|
requires `make`.
|
||||||
|
|
||||||
|
To run busybox tests for all utilities for which busybox has tests
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make busytest
|
||||||
|
```
|
||||||
|
|
||||||
|
To run busybox tests for a few of the available utilities
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make UTILS='UTILITY_1 UTILITY_2' busytest
|
||||||
|
```
|
||||||
|
|
||||||
|
To pass an argument like "-v" to the busybox test runtime
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make UTILS='UTILITY_1 UTILITY_2' RUNTEST_ARGS='-v' busytest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comparing with GNU
|
||||||
|
|
||||||
|
To run uutils against the GNU test suite locally, run the following commands:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
bash util/build-gnu.sh
|
||||||
|
# Build uutils without release optimizations
|
||||||
|
UU_MAKE_PROFILE=debug bash util/build-gnu.sh
|
||||||
|
bash util/run-gnu-test.sh
|
||||||
|
# To run a single test:
|
||||||
|
bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example
|
||||||
|
# To run several tests:
|
||||||
|
bash util/run-gnu-test.sh tests/touch/not-owner.sh tests/rm/no-give-up.sh # for example
|
||||||
|
# If this is a perl (.pl) test, to run in debug:
|
||||||
|
DEBUG=1 bash util/run-gnu-test.sh tests/misc/sm3sum.pl
|
||||||
|
```
|
||||||
|
|
||||||
|
***Tip:*** First time you run `bash util/build-gnu.sh` command, it will provide instructions on how to checkout GNU coreutils repository at the correct release tag. Please follow those instructions and when done, run `bash util/build-gnu.sh` command again.
|
||||||
|
|
||||||
|
Note that GNU test suite relies on individual utilities (not the multicall binary).
|
||||||
|
|
||||||
|
## Code coverage report
|
||||||
|
|
||||||
|
Code coverage report can be generated using [grcov](https://github.com/mozilla/grcov).
|
||||||
|
|
||||||
|
### Using Nightly Rust
|
||||||
|
|
||||||
|
To generate [gcov-based](https://github.com/mozilla/grcov#example-how-to-generate-gcda-files-for-a-rust-project) coverage report
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export CARGO_INCREMENTAL=0
|
||||||
|
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||||
|
export RUSTDOCFLAGS="-Cpanic=abort"
|
||||||
|
cargo build <options...> # e.g., --features feat_os_unix
|
||||||
|
cargo test <options...> # e.g., --features feat_os_unix test_pathchk
|
||||||
|
grcov . -s . --binary-path ./target/debug/ -t html --branch --ignore-not-existing --ignore build.rs --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?\#\[derive\()" -o ./target/debug/coverage/
|
||||||
|
# open target/debug/coverage/index.html in browser
|
||||||
|
```
|
||||||
|
|
||||||
|
if changes are not reflected in the report then run `cargo clean` and run the above commands.
|
||||||
|
|
||||||
|
### Using Stable Rust
|
||||||
|
|
||||||
|
If you are using stable version of Rust that doesn't enable code coverage instrumentation by default
|
||||||
|
then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above.
|
||||||
|
|
||||||
|
## Tips for setting up on Mac
|
||||||
|
|
||||||
|
### C Compiler and linker
|
||||||
|
|
||||||
|
On MacOS you'll need to install C compiler & linker:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
xcode-select --install
|
||||||
|
```
|
||||||
|
|
||||||
|
### MacOS GNU utils
|
||||||
|
|
||||||
|
On MacOS you will need to install [Homebrew](https://docs.brew.sh/Installation) and use it to install the following Homebrew formulas:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
brew install \
|
||||||
|
coreutils \
|
||||||
|
autoconf \
|
||||||
|
gettext \
|
||||||
|
wget \
|
||||||
|
texinfo \
|
||||||
|
xz \
|
||||||
|
automake \
|
||||||
|
gnu-sed \
|
||||||
|
m4 \
|
||||||
|
bison \
|
||||||
|
pre-commit \
|
||||||
|
findutils
|
||||||
|
```
|
||||||
|
|
||||||
|
After installing these Homebrew formulas, please make sure to add the following lines to your `zsh` or `bash` rc file, i.e. `~/.profile` or `~/.zshrc` or `~/.bashrc` ...
|
||||||
|
(assuming Homebrew is installed at default location `/opt/homebrew`):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||||
|
export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
|
||||||
|
export PATH="/opt/homebrew/opt/bison/bin:$PATH"
|
||||||
|
export PATH="/opt/homebrew/opt/findutils/libexec/gnubin:$PATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
Last step is to link Homebrew coreutils version of `timeout` to `/usr/local/bin` (as admin user):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo ln -s /opt/homebrew/bin/timeout /usr/local/bin/timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not forget to either source updated rc file or restart you terminal session to update environment variables.
|
||||||
|
|
||||||
|
## Tips for setting up on Windows
|
||||||
|
|
||||||
|
### MSVC build tools
|
||||||
|
|
||||||
|
On Windows you'll need the MSVC build tools for Visual Studio 2013 or later.
|
||||||
|
|
||||||
|
If you are using `rustup-init.exe` to install Rust toolchain, it will guide you through the process of downloading and installing these prerequisites.
|
||||||
|
|
||||||
|
Otherwise please follow [this guide](https://learn.microsoft.com/en-us/windows/dev-environment/rust/setup).
|
||||||
|
|
||||||
|
### Windows GNU utils
|
||||||
|
|
||||||
|
If you have used [Git for Windows](https://gitforwindows.org) to install `git` on you Windows system you might already have some GNU core utilities installed as part of "GNU Bash" included in Git for Windows package, but it is not a complete package. [This article](https://gist.github.com/evanwill/0207876c3243bbb6863e65ec5dc3f058) provides instruction on how to add more to it.
|
||||||
|
|
||||||
|
Alternatively you can install [Cygwin](https://www.cygwin.com) and/or use [WSL2](https://learn.microsoft.com/en-us/windows/wsl/compare-versions#whats-new-in-wsl-2) to get access to all GNU core utilities on Windows.
|
|
@ -102,7 +102,6 @@ PROGS := \
|
||||||
pwd \
|
pwd \
|
||||||
readlink \
|
readlink \
|
||||||
realpath \
|
realpath \
|
||||||
relpath \
|
|
||||||
rm \
|
rm \
|
||||||
rmdir \
|
rmdir \
|
||||||
seq \
|
seq \
|
||||||
|
|
10
README.md
10
README.md
|
@ -14,7 +14,7 @@
|
||||||
[](https://deps.rs/repo/github/uutils/coreutils)
|
[](https://deps.rs/repo/github/uutils/coreutils)
|
||||||
|
|
||||||
[](https://codecov.io/gh/uutils/coreutils)
|
[](https://codecov.io/gh/uutils/coreutils)
|
||||||

|

|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -54,8 +54,8 @@ that scripts can be easily transferred between platforms.
|
||||||
|
|
||||||
uutils has both user and developer documentation available:
|
uutils has both user and developer documentation available:
|
||||||
|
|
||||||
- [User Manual](https://uutils.github.io/user/)
|
- [User Manual](https://uutils.github.io/coreutils/book/)
|
||||||
- [Developer Documentation](https://uutils.github.io/dev/coreutils/)
|
- [Developer Documentation](https://uutils.github.io/dev/coreutils/) (currently offline, you can use docs.rs in the meantime)
|
||||||
|
|
||||||
Both can also be generated locally, the instructions for that can be found in
|
Both can also be generated locally, the instructions for that can be found in
|
||||||
the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
|
the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
|
||||||
|
@ -71,7 +71,7 @@ the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
|
||||||
### Rust Version
|
### Rust Version
|
||||||
|
|
||||||
uutils follows Rust's release channels and is tested against stable, beta and
|
uutils follows Rust's release channels and is tested against stable, beta and
|
||||||
nightly. The current Minimum Supported Rust Version (MSRV) is `1.64.0`.
|
nightly. The current Minimum Supported Rust Version (MSRV) is `1.70.0`.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ make PREFIX=/my/path uninstall
|
||||||
|
|
||||||
Below is the evolution of how many GNU tests uutils passes. A more detailed
|
Below is the evolution of how many GNU tests uutils passes. A more detailed
|
||||||
breakdown of the GNU test results of the main branch can be found
|
breakdown of the GNU test results of the main branch can be found
|
||||||
[in the user manual](https://uutils.github.io/user/test_coverage.html).
|
[in the user manual](https://uutils.github.io/coreutils/book/test_coverage.html).
|
||||||
|
|
||||||
See <https://github.com/uutils/coreutils/issues/3336> for the main meta bugs
|
See <https://github.com/uutils/coreutils/issues/3336> for the main meta bugs
|
||||||
(many are missing).
|
(many are missing).
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
# Targets that compile
|
|
||||||
|
|
||||||
**Note: this list isn't up to date.**
|
|
||||||
|
|
||||||
This is an auto-generated table showing which binaries compile for each target-triple. Note that this **does not** indicate that they are fully implemented, or that the tests pass.
|
|
||||||
|
|
||||||
|######OS######|###ARCH####|arch|base32|base64|basename|cat|chgrp|chmod|chown|chroot|cksum|comm|cp|csplit|cut|date|df|dircolors|dirname|du|echo|env|expand|expr|factor|false|fmt|fold|groups|hashsum|head|hostid|hostname|id|install|join|kill|link|ln|logname|ls|mkdir|mkfifo|mknod|mktemp|more|mv|nice|nl|nohup|nproc|numfmt|od|paste|pathchk|pinky|printenv|printf|ptx|pwd|readlink|realpath|relpath|rm|rmdir|seq|shred|shuf|sleep|sort|split|stat|stdbuf|sum|sync|tac|tail|tee|test|timeout|touch|tr|true|truncate|tsort|tty|uname|unexpand|uniq|unlink|uptime|users|wc|who|whoami|yes|chcon|pr|dir|vdir|dd|basenc|runcon|
|
|
||||||
|--------------|-----------|----|------|------|--------|---|-----|-----|-----|------|-----|----|--|------|---|----|--|---------|-------|--|----|---|------|----|------|-----|---|----|------|-------|----|------|--------|--|-------|----|----|----|--|-------|--|-----|------|-----|------|----|--|----|--|-----|-----|------|--|-----|-------|-----|--------|------|---|---|--------|--------|-------|--|-----|---|-----|----|-----|----|-----|----|------|---|----|---|----|---|----|-------|-----|--|----|--------|-----|---|-----|--------|----|------|------|-----|--|---|------|---|-----|--|---|----|--|------|------|
|
|
||||||
|linux-gnu|aarch64|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
|
||||||
|linux-gnu|i686|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
|
||||||
|linux-gnu|powerpc64|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
|
||||||
|linux-gnu|riscv64gc| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
||||||
|linux-gnu|x86_64|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
|
||||||
|windows-msvc|aarch64|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y| |y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y| |y|y|y| |y| |y| | |y|y|y|y|y|y|y|y|
|
|
||||||
|windows-gnu|i686|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y|y|y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y| |y| |y| |y|y|y|y|y|y|y|y|y|
|
|
||||||
|windows-msvc|i686|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y|y|y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y| |y|y|y| |y| |y| |y|y|y|y|y|y|y|y|y|
|
|
||||||
|windows-gnu|x86_64|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y|y|y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y| |y| |y| |y|y|y|y|y|y|y|y|y|
|
|
||||||
|windows-msvc|x86_64|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y|y|y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y| |y|y|y| |y| |y| |y|y|y|y|y|y|y|y|y|
|
|
||||||
|apple MacOS|x86_64|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
|
||||||
|freebsd|x86_64|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
|
||||||
|netbsd|x86_64|y|y|y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y| |y|y| |y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y| | |y| |y|y|y|y|y|y|y|y|y|
|
|
||||||
|android|aarch64|y|y|y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y| |y|y| |y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y| |y|y|y|y| |y|y|y|y|y|y| |y|y|y| | |y| |y|y|y|y|y|y|y|y|y|
|
|
||||||
|android|x86_64|y|y|y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y| |y|y| |y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y| |y|y|y|y| |y|y|y|y|y|y| |y|y|y| | |y| |y|y|y|y|y|y|y|y|y|
|
|
||||||
|solaris|x86_64| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
||||||
|wasi|wasm32| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
||||||
|redox|x86_64| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
||||||
|fuchsia|aarch64| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
||||||
|fuchsia|x86_64| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
|
6
build.rs
6
build.rs
|
@ -1,3 +1,8 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (vars) krate
|
// spell-checker:ignore (vars) krate
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -40,6 +45,7 @@ pub fn main() {
|
||||||
mf.write_all(
|
mf.write_all(
|
||||||
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
|
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
|
||||||
\n\
|
\n\
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
|
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)
|
)
|
||||||
|
|
15
deny.toml
15
deny.toml
|
@ -59,9 +59,12 @@ highlight = "all"
|
||||||
# spell-checker: disable
|
# spell-checker: disable
|
||||||
skip = [
|
skip = [
|
||||||
# procfs
|
# procfs
|
||||||
{ name = "rustix", version = "0.36.14" },
|
{ name = "rustix", version = "0.36.16" },
|
||||||
# rustix
|
# rustix
|
||||||
{ name = "linux-raw-sys", version = "0.1.4" },
|
{ name = "linux-raw-sys", version = "0.1.4" },
|
||||||
|
{ name = "linux-raw-sys", version = "0.3.8" },
|
||||||
|
# terminal_size
|
||||||
|
{ name = "rustix", version = "0.37.26" },
|
||||||
# various crates
|
# various crates
|
||||||
{ name = "windows-sys", version = "0.45.0" },
|
{ name = "windows-sys", version = "0.45.0" },
|
||||||
# windows-sys
|
# windows-sys
|
||||||
|
@ -80,12 +83,14 @@ skip = [
|
||||||
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
|
{ name = "windows_x86_64_gnullvm", version = "0.42.2" },
|
||||||
# windows-targets
|
# windows-targets
|
||||||
{ name = "windows_x86_64_msvc", version = "0.42.2" },
|
{ name = "windows_x86_64_msvc", version = "0.42.2" },
|
||||||
# tempfile
|
|
||||||
{ name = "redox_syscall", version = "0.3.5" },
|
|
||||||
# cpp_macros
|
|
||||||
{ name = "aho-corasick", version = "0.7.19" },
|
|
||||||
# various crates
|
# various crates
|
||||||
{ name = "syn", version = "1.0.109" },
|
{ name = "syn", version = "1.0.109" },
|
||||||
|
# various crates
|
||||||
|
{ name = "bitflags", version = "1.3.2" },
|
||||||
|
# various crates
|
||||||
|
{ name = "redox_syscall", version = "0.3.5" },
|
||||||
|
# clap_builder, textwrap
|
||||||
|
{ name = "terminal_size", version = "0.2.6" },
|
||||||
]
|
]
|
||||||
# spell-checker: enable
|
# spell-checker: enable
|
||||||
|
|
||||||
|
|
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
book
|
book
|
||||||
src/utils
|
src/utils
|
||||||
src/SUMMARY.md
|
src/SUMMARY.md
|
||||||
|
src/platform_table.md
|
||||||
tldr.zip
|
tldr.zip
|
|
@ -1,21 +1,21 @@
|
||||||
target,arch,base32,base64,basename,cat,chgrp,chmod,chown,chroot,cksum,comm,cp,csplit,cut,date,df,dircolors,dirname,du,echo,env,expand,expr,factor,false,fmt,fold,groups,hashsum,head,hostid,hostname,id,install,join,kill,link,ln,logname,ls,mkdir,mkfifo,mknod,mktemp,more,mv,nice,nl,nohup,nproc,numfmt,od,paste,pathchk,pinky,printenv,printf,ptx,pwd,readlink,realpath,relpath,rm,rmdir,seq,shred,shuf,sleep,sort,split,stat,stdbuf,sum,sync,tac,tail,tee,test,timeout,touch,tr,true,truncate,tsort,tty,uname,unexpand,uniq,unlink,uptime,users,wc,who,whoami,yes,chcon,pr,dir,vdir,dd,basenc,runcon
|
target,arch,base32,base64,basename,cat,chgrp,chmod,chown,chroot,cksum,comm,cp,csplit,cut,date,df,dircolors,dirname,du,echo,env,expand,expr,factor,false,fmt,fold,groups,hashsum,head,hostid,hostname,id,install,join,kill,link,ln,logname,ls,mkdir,mkfifo,mknod,mktemp,more,mv,nice,nl,nohup,nproc,numfmt,od,paste,pathchk,pinky,printenv,printf,ptx,pwd,readlink,realpath,rm,rmdir,seq,shred,shuf,sleep,sort,split,stat,stdbuf,sum,sync,tac,tail,tee,test,timeout,touch,tr,true,truncate,tsort,tty,uname,unexpand,uniq,unlink,uptime,users,wc,who,whoami,yes,chcon,pr,dir,vdir,dd,basenc,runcon
|
||||||
aarch64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
aarch64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
i686-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
i686-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
powerpc64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
powerpc64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
riscv64gc-unknown-linux-gnu,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
riscv64gc-unknown-linux-gnu,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
||||||
x86_64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
x86_64-unknown-linux-gnu,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
aarch64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,101,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,101,0,0,0,0,0,0,0,0
|
aarch64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,101,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,101,0,0,0,0,0,0,0,0
|
||||||
i686-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
i686-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
||||||
i686-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
i686-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
||||||
x86_64-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
x86_64-pc-windows-gnu,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
||||||
x86_64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
x86_64-pc-windows-msvc,0,0,0,0,0,101,101,101,101,0,0,0,0,0,0,0,0,0,101,0,0,0,101,0,0,0,0,101,0,0,0,0,101,101,0,101,0,0,0,0,0,101,101,0,0,0,101,0,101,0,0,0,0,101,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,101,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,101,0,101,0,101,0,0,0,0,0,0,0,0,0
|
||||||
x86_64-apple-darwin,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
x86_64-apple-darwin,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
x86_64-unknown-freebsd,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
x86_64-unknown-freebsd,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
x86_64-unknown-netbsd,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0
|
x86_64-unknown-netbsd,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0
|
||||||
aarch64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0
|
aarch64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0
|
||||||
x86_64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0
|
x86_64-linux-android,0,0,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,0,0,101,0,0,0,0,101,0,0,0,0,0,0,101,0,0,0,101,101,0,101,0,0,0,0,0,0,0,0,0
|
||||||
x86_64-sun-solaris,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
x86_64-sun-solaris,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
||||||
wasm32-wasi,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
wasm32-wasi,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
||||||
x86_64-unknown-redox,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
x86_64-unknown-redox,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
||||||
aarch64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
aarch64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
||||||
x86_64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
x86_64-fuchsia,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101
|
||||||
|
|
|
|
@ -77,4 +77,5 @@ third way: `--long`.
|
||||||
|
|
||||||
## `du`
|
## `du`
|
||||||
|
|
||||||
`du` allows `birth` and `creation` as values for the `--time` argument to show the creation time.
|
`du` allows `birth` and `creation` as values for the `--time` argument to show the creation time. It
|
||||||
|
also provides a `-v`/`--verbose` flag.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- spell-checker:ignore pacman pamac nixpkgs openmandriva -->
|
<!-- spell-checker:ignore pacman pamac nixpkgs openmandriva conda -->
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
|
@ -139,6 +139,16 @@ pkg install rust-coreutils
|
||||||
scoop install uutils-coreutils
|
scoop install uutils-coreutils
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Alternative installers
|
||||||
|
|
||||||
|
### Conda
|
||||||
|
|
||||||
|
[Conda package](https://anaconda.org/conda-forge/uutils-coreutils)
|
||||||
|
|
||||||
|
```
|
||||||
|
conda install -c conda-forge uutils-coreutils
|
||||||
|
```
|
||||||
|
|
||||||
## Non-standard packages
|
## Non-standard packages
|
||||||
|
|
||||||
### `coreutils-hybrid` (AUR)
|
### `coreutils-hybrid` (AUR)
|
||||||
|
|
45
docs/src/platforms.md
Normal file
45
docs/src/platforms.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Platform support
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD033 -->
|
||||||
|
|
||||||
|
uutils aims to be as "universal" as possible, meaning that we try to support
|
||||||
|
many platforms. However, it is infeasible for us to guarantee that every
|
||||||
|
platform works. Just like Rust itself, we therefore have multiple tiers of
|
||||||
|
platform support, with different guarantees. We support two tiers of platforms:
|
||||||
|
|
||||||
|
- **Tier 1**: All applicable utils are compiled and tested in CI for these
|
||||||
|
platforms.
|
||||||
|
- **Tier 2**: These platforms are supported but not actively tested. We do accept
|
||||||
|
fixes for these platforms.
|
||||||
|
|
||||||
|
> **Note**: The tiers are dictated by our CI. We would happily accept a job
|
||||||
|
> in the CI for testing more platforms, bumping those platforms to tier 1.
|
||||||
|
|
||||||
|
## Platforms per tier
|
||||||
|
|
||||||
|
The platforms in tier 1 and the platforms that we test in CI are listed below.
|
||||||
|
|
||||||
|
| Operating system | Tested targets |
|
||||||
|
| ---------------- | -------------- |
|
||||||
|
| **Linux** | `x86_64-unknown-linux-gnu` <br> `x86_64-unknown-linux-musl` <br> `arm-unknown-linux-gnueabihf` <br> `i686-unknown-linux-gnu` <br> `aarch64-unknown-linux-gnu` |
|
||||||
|
| **macOS** | `x86_64-apple-darwin` |
|
||||||
|
| **Windows** | `i686-pc-windows-msvc` <br> `x86_64-pc-windows-gnu` <br> `x86_64-pc-windows-msvc` |
|
||||||
|
| **FreeBSD** | `x86_64-unknown-freebsd` |
|
||||||
|
| **Android** | `i686-linux-android` |
|
||||||
|
|
||||||
|
The platforms in tier 2 are more vague, but include:
|
||||||
|
|
||||||
|
- untested variations of the platforms above,
|
||||||
|
- Redox OS,
|
||||||
|
- and BSDs such as OpenBSD, NetBSD & DragonFlyBSD.
|
||||||
|
|
||||||
|
## Utility compatibility per platform
|
||||||
|
|
||||||
|
Not all utils work on every platform. For instance, `chgrp` is not supported on
|
||||||
|
Windows, because Windows does not have the concept of groups. Below is a full table
|
||||||
|
detailing which utilities are supported for the tier 1 platforms.
|
||||||
|
|
||||||
|
Note that for some utilities, not all functionality is supported on each
|
||||||
|
platform. This is documented per utility.
|
||||||
|
|
||||||
|
{{ #include platform_table.md }}
|
|
@ -9,12 +9,14 @@ cargo-fuzz = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4"
|
||||||
|
libc = "0.2"
|
||||||
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
|
|
||||||
[dependencies.uucore]
|
uucore = { path = "../src/uucore/" }
|
||||||
path = "../src/uucore/"
|
uu_date = { path = "../src/uu/date/" }
|
||||||
|
uu_test = { path = "../src/uu/test/" }
|
||||||
|
uu_expr = { path = "../src/uu/expr/" }
|
||||||
|
|
||||||
[dependencies.uu_date]
|
|
||||||
path = "../src/uu/date/"
|
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
# Prevent this from interfering with workspaces
|
||||||
[workspace]
|
[workspace]
|
||||||
|
@ -26,6 +28,18 @@ path = "fuzz_targets/fuzz_date.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_expr"
|
||||||
|
path = "fuzz_targets/fuzz_expr.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_test"
|
||||||
|
path = "fuzz_targets/fuzz_test.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "fuzz_parse_glob"
|
name = "fuzz_parse_glob"
|
||||||
path = "fuzz_targets/fuzz_parse_glob.rs"
|
path = "fuzz_targets/fuzz_parse_glob.rs"
|
||||||
|
|
107
fuzz/fuzz_targets/fuzz_common.rs
Normal file
107
fuzz/fuzz_targets/fuzz_common.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
|
use libc::{dup, dup2, STDOUT_FILENO};
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::io;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::sync::{atomic::AtomicBool, Once};
|
||||||
|
|
||||||
|
static CHECK_GNU: Once = Once::new();
|
||||||
|
static IS_GNU: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> {
|
||||||
|
CHECK_GNU.call_once(|| {
|
||||||
|
let version_output = Command::new(cmd_path).arg("--version").output().unwrap();
|
||||||
|
|
||||||
|
println!("version_output {:#?}", version_output);
|
||||||
|
|
||||||
|
let version_str = String::from_utf8_lossy(&version_output.stdout).to_string();
|
||||||
|
if version_str.contains("GNU coreutils") {
|
||||||
|
IS_GNU.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if IS_GNU.load(Ordering::Relaxed) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
panic!("Not the GNU implementation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_and_run_uumain<F>(args: &[OsString], uumain_function: F) -> (String, i32)
|
||||||
|
where
|
||||||
|
F: FnOnce(std::vec::IntoIter<OsString>) -> i32,
|
||||||
|
{
|
||||||
|
let uumain_exit_status;
|
||||||
|
|
||||||
|
let original_stdout_fd = unsafe { dup(STDOUT_FILENO) };
|
||||||
|
println!("Running test {:?}", &args[1..]);
|
||||||
|
let mut pipe_fds = [-1; 2];
|
||||||
|
unsafe { libc::pipe(pipe_fds.as_mut_ptr()) };
|
||||||
|
|
||||||
|
{
|
||||||
|
unsafe { dup2(pipe_fds[1], STDOUT_FILENO) };
|
||||||
|
uumain_exit_status = uumain_function(args.to_owned().into_iter());
|
||||||
|
unsafe { dup2(original_stdout_fd, STDOUT_FILENO) };
|
||||||
|
unsafe { libc::close(original_stdout_fd) };
|
||||||
|
}
|
||||||
|
unsafe { libc::close(pipe_fds[1]) };
|
||||||
|
|
||||||
|
let mut captured_output = Vec::new();
|
||||||
|
let mut read_buffer = [0; 1024];
|
||||||
|
loop {
|
||||||
|
let bytes_read = unsafe {
|
||||||
|
libc::read(
|
||||||
|
pipe_fds[0],
|
||||||
|
read_buffer.as_mut_ptr() as *mut libc::c_void,
|
||||||
|
read_buffer.len(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if bytes_read <= 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { libc::close(pipe_fds[0]) };
|
||||||
|
|
||||||
|
let my_output = String::from_utf8_lossy(&captured_output)
|
||||||
|
.to_string()
|
||||||
|
.trim()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
(my_output, uumain_exit_status)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_gnu_cmd(
|
||||||
|
cmd_path: &str,
|
||||||
|
args: &[OsString],
|
||||||
|
check_gnu: bool,
|
||||||
|
) -> Result<(String, i32), io::Error> {
|
||||||
|
if check_gnu {
|
||||||
|
is_gnu_cmd(cmd_path)?; // Check if it's a GNU implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut command = Command::new(cmd_path);
|
||||||
|
for arg in args {
|
||||||
|
command.arg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = command.output()?;
|
||||||
|
let exit_code = output.status.code().unwrap_or(-1);
|
||||||
|
if output.status.success() || !check_gnu {
|
||||||
|
Ok((
|
||||||
|
String::from_utf8_lossy(&output.stdout).to_string(),
|
||||||
|
exit_code,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GNU command execution failed with exit code {}", exit_code),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,6 @@ fuzz_target!(|data: &[u8]| {
|
||||||
let args = data
|
let args = data
|
||||||
.split(|b| *b == delim)
|
.split(|b| *b == delim)
|
||||||
.filter_map(|e| std::str::from_utf8(e).ok())
|
.filter_map(|e| std::str::from_utf8(e).ok())
|
||||||
.map(|e| OsString::from(e));
|
.map(OsString::from);
|
||||||
uumain(args);
|
uumain(args);
|
||||||
});
|
});
|
||||||
|
|
120
fuzz/fuzz_targets/fuzz_expr.rs
Normal file
120
fuzz/fuzz_targets/fuzz_expr.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
// spell-checker:ignore parens
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use uu_expr::uumain;
|
||||||
|
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::{env, ffi::OsString};
|
||||||
|
|
||||||
|
mod fuzz_common;
|
||||||
|
use crate::fuzz_common::{generate_and_run_uumain, run_gnu_cmd};
|
||||||
|
|
||||||
|
static CMD_PATH: &str = "expr";
|
||||||
|
|
||||||
|
fn generate_random_string(max_length: usize) -> String {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let valid_utf8: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
.chars()
|
||||||
|
.collect();
|
||||||
|
let invalid_utf8 = [0xC3, 0x28]; // Invalid UTF-8 sequence
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
for _ in 0..rng.gen_range(1..=max_length) {
|
||||||
|
if rng.gen_bool(0.9) {
|
||||||
|
let ch = valid_utf8.choose(&mut rng).unwrap();
|
||||||
|
result.push(*ch);
|
||||||
|
} else {
|
||||||
|
let ch = invalid_utf8.choose(&mut rng).unwrap();
|
||||||
|
if let Some(c) = char::from_u32(*ch as u32) {
|
||||||
|
result.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_expr(max_depth: u32) -> String {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let ops = ["+", "-", "*", "/", "%", "<", ">", "=", "&", "|"];
|
||||||
|
|
||||||
|
let mut expr = String::new();
|
||||||
|
let mut depth = 0;
|
||||||
|
let mut last_was_operator = false;
|
||||||
|
|
||||||
|
while depth <= max_depth {
|
||||||
|
if last_was_operator || depth == 0 {
|
||||||
|
// Add a number
|
||||||
|
expr.push_str(&rng.gen_range(1..=100).to_string());
|
||||||
|
last_was_operator = false;
|
||||||
|
} else {
|
||||||
|
// 90% chance to add an operator followed by a number
|
||||||
|
if rng.gen_bool(0.9) {
|
||||||
|
let op = *ops.choose(&mut rng).unwrap();
|
||||||
|
expr.push_str(&format!(" {} ", op));
|
||||||
|
last_was_operator = true;
|
||||||
|
}
|
||||||
|
// 10% chance to add a random string (potentially invalid syntax)
|
||||||
|
else {
|
||||||
|
let random_str = generate_random_string(rng.gen_range(1..=10));
|
||||||
|
expr.push_str(&random_str);
|
||||||
|
last_was_operator = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the expression ends with a number if it ended with an operator
|
||||||
|
if last_was_operator {
|
||||||
|
expr.push_str(&rng.gen_range(1..=100).to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzz_target!(|_data: &[u8]| {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let expr = generate_expr(rng.gen_range(0..=20));
|
||||||
|
let mut args = vec![OsString::from("expr")];
|
||||||
|
args.extend(expr.split_whitespace().map(OsString::from));
|
||||||
|
|
||||||
|
let (rust_output, uumain_exit_code) = generate_and_run_uumain(&args, uumain);
|
||||||
|
|
||||||
|
// Use C locale to avoid false positives, like in https://github.com/uutils/coreutils/issues/5378,
|
||||||
|
// because uutils expr doesn't support localization yet
|
||||||
|
// TODO remove once uutils expr supports localization
|
||||||
|
env::set_var("LC_COLLATE", "C");
|
||||||
|
|
||||||
|
// Run GNU expr with the provided arguments and compare the output
|
||||||
|
match run_gnu_cmd(CMD_PATH, &args[1..], true) {
|
||||||
|
Ok((gnu_output, gnu_exit_code)) => {
|
||||||
|
let gnu_output = gnu_output.trim().to_owned();
|
||||||
|
if uumain_exit_code != gnu_exit_code {
|
||||||
|
println!("Expression: {}", expr);
|
||||||
|
println!("Rust code: {}", uumain_exit_code);
|
||||||
|
println!("GNU code: {}", gnu_exit_code);
|
||||||
|
panic!("Different error codes");
|
||||||
|
}
|
||||||
|
if rust_output == gnu_output {
|
||||||
|
println!(
|
||||||
|
"Outputs matched for expression: {} => Result: {}",
|
||||||
|
expr, rust_output
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("Expression: {}", expr);
|
||||||
|
println!("Rust output: {}", rust_output);
|
||||||
|
println!("GNU output: {}", gnu_output);
|
||||||
|
panic!("Different output between Rust & GNU");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
println!("GNU expr execution failed for expression: {}", expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -5,6 +5,6 @@ use uucore::parse_glob;
|
||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
fuzz_target!(|data: &[u8]| {
|
||||||
if let Ok(s) = std::str::from_utf8(data) {
|
if let Ok(s) = std::str::from_utf8(data) {
|
||||||
_ = parse_glob::from_str(s)
|
_ = parse_glob::from_str(s);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
234
fuzz/fuzz_targets/fuzz_test.rs
Normal file
234
fuzz/fuzz_targets/fuzz_test.rs
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
// spell-checker:ignore STRINGSTRING INTEGERINTEGER FILEFILE
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use uu_test::uumain;
|
||||||
|
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
|
||||||
|
mod fuzz_common;
|
||||||
|
use crate::fuzz_common::{generate_and_run_uumain, run_gnu_cmd};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
enum ArgType {
|
||||||
|
STRING,
|
||||||
|
STRINGSTRING,
|
||||||
|
INTEGER,
|
||||||
|
INTEGERINTEGER,
|
||||||
|
FILE,
|
||||||
|
FILEFILE,
|
||||||
|
// Add any other types as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
static CMD_PATH: &str = "test";
|
||||||
|
|
||||||
|
fn generate_random_string(max_length: usize) -> String {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let valid_utf8: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
.chars()
|
||||||
|
.collect();
|
||||||
|
let invalid_utf8 = [0xC3, 0x28]; // Invalid UTF-8 sequence
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
for _ in 0..rng.gen_range(1..=max_length) {
|
||||||
|
if rng.gen_bool(0.9) {
|
||||||
|
let ch = valid_utf8.choose(&mut rng).unwrap();
|
||||||
|
result.push(*ch);
|
||||||
|
} else {
|
||||||
|
let ch = invalid_utf8.choose(&mut rng).unwrap();
|
||||||
|
if let Some(c) = char::from_u32(*ch as u32) {
|
||||||
|
result.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct TestArg {
|
||||||
|
arg: String,
|
||||||
|
arg_type: ArgType,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_random_path(rng: &mut dyn rand::RngCore) -> &'static str {
|
||||||
|
match rng.gen_range(0..=3) {
|
||||||
|
0 => "/dev/null",
|
||||||
|
1 => "/dev/random",
|
||||||
|
2 => "/tmp",
|
||||||
|
_ => "/dev/urandom",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_test_args() -> Vec<TestArg> {
|
||||||
|
vec![
|
||||||
|
TestArg {
|
||||||
|
arg: "-z".to_string(),
|
||||||
|
arg_type: ArgType::STRING,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-n".to_string(),
|
||||||
|
arg_type: ArgType::STRING,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "=".to_string(),
|
||||||
|
arg_type: ArgType::STRINGSTRING,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "!=".to_string(),
|
||||||
|
arg_type: ArgType::STRINGSTRING,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-eq".to_string(),
|
||||||
|
arg_type: ArgType::INTEGERINTEGER,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-ne".to_string(),
|
||||||
|
arg_type: ArgType::INTEGERINTEGER,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-gt".to_string(),
|
||||||
|
arg_type: ArgType::INTEGERINTEGER,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-ge".to_string(),
|
||||||
|
arg_type: ArgType::INTEGERINTEGER,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-lt".to_string(),
|
||||||
|
arg_type: ArgType::INTEGERINTEGER,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-le".to_string(),
|
||||||
|
arg_type: ArgType::INTEGERINTEGER,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-f".to_string(),
|
||||||
|
arg_type: ArgType::FILE,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-d".to_string(),
|
||||||
|
arg_type: ArgType::FILE,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-e".to_string(),
|
||||||
|
arg_type: ArgType::FILE,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-ef".to_string(),
|
||||||
|
arg_type: ArgType::FILEFILE,
|
||||||
|
},
|
||||||
|
TestArg {
|
||||||
|
arg: "-nt".to_string(),
|
||||||
|
arg_type: ArgType::FILEFILE,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_test_arg() -> String {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let test_args = generate_test_args();
|
||||||
|
let mut arg = String::new();
|
||||||
|
|
||||||
|
let choice = rng.gen_range(0..=5);
|
||||||
|
|
||||||
|
match choice {
|
||||||
|
0 => {
|
||||||
|
arg.push_str(&rng.gen_range(-100..=100).to_string());
|
||||||
|
}
|
||||||
|
1..=3 => {
|
||||||
|
let test_arg = test_args
|
||||||
|
.choose(&mut rng)
|
||||||
|
.expect("Failed to choose a random test argument");
|
||||||
|
if test_arg.arg_type == ArgType::INTEGER {
|
||||||
|
arg.push_str(&format!(
|
||||||
|
"{} {} {}",
|
||||||
|
&rng.gen_range(-100..=100).to_string(),
|
||||||
|
test_arg.arg,
|
||||||
|
&rng.gen_range(-100..=100).to_string()
|
||||||
|
));
|
||||||
|
} else if test_arg.arg_type == ArgType::STRINGSTRING {
|
||||||
|
let random_str = generate_random_string(rng.gen_range(1..=10));
|
||||||
|
let random_str2 = generate_random_string(rng.gen_range(1..=10));
|
||||||
|
|
||||||
|
arg.push_str(&format!(
|
||||||
|
"{} {} {}",
|
||||||
|
&random_str, test_arg.arg, &random_str2
|
||||||
|
));
|
||||||
|
} else if test_arg.arg_type == ArgType::STRING {
|
||||||
|
let random_str = generate_random_string(rng.gen_range(1..=10));
|
||||||
|
arg.push_str(&format!("{} {}", test_arg.arg, &random_str));
|
||||||
|
} else if test_arg.arg_type == ArgType::FILEFILE {
|
||||||
|
let path = generate_random_path(&mut rng);
|
||||||
|
let path2 = generate_random_path(&mut rng);
|
||||||
|
arg.push_str(&format!("{} {} {}", path, test_arg.arg, path2));
|
||||||
|
} else if test_arg.arg_type == ArgType::FILE {
|
||||||
|
let path = generate_random_path(&mut rng);
|
||||||
|
arg.push_str(&format!("{} {}", test_arg.arg, path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let random_str = generate_random_string(rng.gen_range(1..=10));
|
||||||
|
arg.push_str(&random_str);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let path = generate_random_path(&mut rng);
|
||||||
|
|
||||||
|
let file_test_args: Vec<TestArg> = test_args
|
||||||
|
.iter()
|
||||||
|
.filter(|ta| ta.arg_type == ArgType::FILE)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Some(test_arg) = file_test_args.choose(&mut rng) {
|
||||||
|
arg.push_str(&format!("{}{}", test_arg.arg, path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzz_target!(|_data: &[u8]| {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let max_args = rng.gen_range(1..=6);
|
||||||
|
let mut args = vec![OsString::from("test")];
|
||||||
|
|
||||||
|
for _ in 0..max_args {
|
||||||
|
args.push(OsString::from(generate_test_arg()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (rust_output, uumain_exit_status) = generate_and_run_uumain(&args, uumain);
|
||||||
|
|
||||||
|
// Run GNU test with the provided arguments and compare the output
|
||||||
|
match run_gnu_cmd(CMD_PATH, &args[1..], false) {
|
||||||
|
Ok((gnu_output, gnu_exit_status)) => {
|
||||||
|
let gnu_output = gnu_output.trim().to_owned();
|
||||||
|
println!("gnu_exit_status {}", gnu_exit_status);
|
||||||
|
println!("uumain_exit_status {}", uumain_exit_status);
|
||||||
|
if rust_output != gnu_output || uumain_exit_status != gnu_exit_status {
|
||||||
|
println!("Discrepancy detected!");
|
||||||
|
println!("Test: {:?}", &args[1..]);
|
||||||
|
println!("My output: {}", rust_output);
|
||||||
|
println!("GNU output: {}", gnu_output);
|
||||||
|
println!("My exit status: {}", uumain_exit_status);
|
||||||
|
println!("GNU exit status: {}", gnu_exit_status);
|
||||||
|
panic!();
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Outputs and exit statuses matched for expression {:?}",
|
||||||
|
&args[1..]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
println!("GNU test execution failed for expression {:?}", &args[1..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -2,8 +2,13 @@
|
||||||
"project": {
|
"project": {
|
||||||
"name": "uutils coreutils"
|
"name": "uutils coreutils"
|
||||||
},
|
},
|
||||||
|
"build": {
|
||||||
|
"path_prefix": "coreutils"
|
||||||
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"changelog": true
|
"changelog": {
|
||||||
|
"read_changelog_file": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"theme": "light",
|
"theme": "light",
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Michael Gehring <mg@ebfe.org>
|
|
||||||
//
|
|
||||||
// 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.
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ fn main() -> io::Result<()> {
|
||||||
[Introduction](index.md)\n\
|
[Introduction](index.md)\n\
|
||||||
* [Installation](installation.md)\n\
|
* [Installation](installation.md)\n\
|
||||||
* [Build from source](build.md)\n\
|
* [Build from source](build.md)\n\
|
||||||
|
* [Platform support](platforms.md)\n\
|
||||||
* [Contributing](contributing.md)\n\
|
* [Contributing](contributing.md)\n\
|
||||||
* [GNU test coverage](test_coverage.md)\n\
|
* [GNU test coverage](test_coverage.md)\n\
|
||||||
* [Extensions](extensions.md)\n\
|
* [Extensions](extensions.md)\n\
|
||||||
|
@ -53,7 +54,7 @@ fn main() -> io::Result<()> {
|
||||||
println!("Gathering utils per platform");
|
println!("Gathering utils per platform");
|
||||||
let utils_per_platform = {
|
let utils_per_platform = {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for platform in ["unix", "macos", "windows"] {
|
for platform in ["unix", "macos", "windows", "unix_android"] {
|
||||||
let platform_utils: Vec<String> = String::from_utf8(
|
let platform_utils: Vec<String> = String::from_utf8(
|
||||||
std::process::Command::new("./util/show-utils.sh")
|
std::process::Command::new("./util/show-utils.sh")
|
||||||
.arg(format!("--features=feat_os_{}", platform))
|
.arg(format!("--features=feat_os_{}", platform))
|
||||||
|
@ -61,6 +62,7 @@ fn main() -> io::Result<()> {
|
||||||
.stdout,
|
.stdout,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.trim()
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.map(ToString::to_string)
|
.map(ToString::to_string)
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -75,6 +77,7 @@ fn main() -> io::Result<()> {
|
||||||
.stdout,
|
.stdout,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.trim()
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.map(ToString::to_string)
|
.map(ToString::to_string)
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -83,9 +86,47 @@ fn main() -> io::Result<()> {
|
||||||
map
|
map
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Writing to utils");
|
|
||||||
let mut utils = utils.entries().collect::<Vec<_>>();
|
let mut utils = utils.entries().collect::<Vec<_>>();
|
||||||
utils.sort();
|
utils.sort();
|
||||||
|
|
||||||
|
println!("Writing util per platform table");
|
||||||
|
{
|
||||||
|
let mut platform_table_file = File::create("docs/src/platform_table.md").unwrap();
|
||||||
|
|
||||||
|
// sum, cksum, b2sum, etc. are all available on all platforms, but not in the data structure
|
||||||
|
// otherwise, we check the map for the util name.
|
||||||
|
let check_supported = |name: &str, platform: &str| {
|
||||||
|
if name.ends_with("sum") || utils_per_platform[platform].iter().any(|u| u == name) {
|
||||||
|
"✓"
|
||||||
|
} else {
|
||||||
|
" "
|
||||||
|
}
|
||||||
|
};
|
||||||
|
writeln!(
|
||||||
|
platform_table_file,
|
||||||
|
"| util | Linux | macOS | Windows | FreeBSD | Android |\n\
|
||||||
|
| ---------------- | ----- | ----- | ------- | ------- | ------- |"
|
||||||
|
)?;
|
||||||
|
for (&name, _) in &utils {
|
||||||
|
if name == "[" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// The alignment is not necessary, but makes the output a bit more
|
||||||
|
// pretty when viewed as plain markdown.
|
||||||
|
writeln!(
|
||||||
|
platform_table_file,
|
||||||
|
"| {:<16} | {:<5} | {:<5} | {:<7} | {:<7} | {:<7} |",
|
||||||
|
format!("**{name}**"),
|
||||||
|
check_supported(name, "linux"),
|
||||||
|
check_supported(name, "macos"),
|
||||||
|
check_supported(name, "windows"),
|
||||||
|
check_supported(name, "unix"),
|
||||||
|
check_supported(name, "unix_android"),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Writing to utils");
|
||||||
for (&name, (_, command)) in utils {
|
for (&name, (_, command)) in utils {
|
||||||
if name == "[" {
|
if name == "[" {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_arch"
|
name = "uu_arch"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "arch ~ (uutils) display machine architecture"
|
description = "arch ~ (uutils) display machine architecture"
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Smigle00 <smigle00@gmail.com>
|
|
||||||
// (c) Jian Zeng <anonymousknight96 AT 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.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_base32"
|
name = "uu_base32"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
|
description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
// For the full copyright and license information, please view the LICENSE
|
||||||
//
|
// file that was distributed with this source code.
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
|
||||||
// that was distributed with this source code.
|
|
||||||
|
|
||||||
use std::io::{stdin, Read};
|
use std::io::{stdin, Read};
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Jordy Dickinson <jordy.dickinson@gmail.com>
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
// file that was distributed with this source code.
|
||||||
// (c) Alex Lyon <arcterus@mail.com>
|
|
||||||
//
|
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
|
||||||
// that was distributed with this source code.
|
|
||||||
|
|
||||||
use std::io::{stdout, Read, Write};
|
use std::io::{stdout, Read, Write};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_base64"
|
name = "uu_base64"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
|
description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Jordy Dickinson <jordy.dickinson@gmail.com>
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
// file that was distributed with this source code.
|
||||||
//
|
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
|
||||||
// that was distributed with this source code.
|
|
||||||
|
|
||||||
use uu_base32::base_common;
|
use uu_base32::base_common;
|
||||||
pub use uu_base32::uu_app;
|
pub use uu_base32::uu_app;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_basename"
|
name = "uu_basename"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
|
description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Jimmy Lu <jimmy.lu.2011@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.
|
||||||
|
|
||||||
|
@ -11,6 +9,7 @@ use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use std::path::{is_separator, PathBuf};
|
use std::path::{is_separator, PathBuf};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{UResult, UUsageError};
|
use uucore::error::{UResult, UUsageError};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
static ABOUT: &str = help_about!("basename.md");
|
static ABOUT: &str = help_about!("basename.md");
|
||||||
|
@ -54,9 +53,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
return Err(UUsageError::new(1, "missing operand".to_string()));
|
return Err(UUsageError::new(1, "missing operand".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
|
||||||
|
|
||||||
let opt_suffix = matches.get_one::<String>(options::SUFFIX).is_some();
|
let opt_suffix = matches.get_one::<String>(options::SUFFIX).is_some();
|
||||||
let opt_multiple = matches.get_flag(options::MULTIPLE);
|
let opt_multiple = matches.get_flag(options::MULTIPLE);
|
||||||
let opt_zero = matches.get_flag(options::ZERO);
|
|
||||||
let multiple_paths = opt_suffix || opt_multiple;
|
let multiple_paths = opt_suffix || opt_multiple;
|
||||||
let name_args_count = matches
|
let name_args_count = matches
|
||||||
.get_many::<String>(options::NAME)
|
.get_many::<String>(options::NAME)
|
||||||
|
@ -105,7 +105,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let line_ending = if opt_zero { "\0" } else { "\n" };
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
print!("{}{}", basename(path, suffix), line_ending);
|
print!("{}{}", basename(path, suffix), line_ending);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_basenc"
|
name = "uu_basenc"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "basenc ~ (uutils) decode/encode input"
|
description = "basenc ~ (uutils) decode/encode input"
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Jordy Dickinson <jordy.dickinson@gmail.com>
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
// file that was distributed with this source code.
|
||||||
//
|
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
|
||||||
// that was distributed with this source code.
|
|
||||||
|
|
||||||
//spell-checker:ignore (args) lsbf msbf
|
//spell-checker:ignore (args) lsbf msbf
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cat"
|
name = "uu_cat"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "cat ~ (uutils) concatenate and display input"
|
description = "cat ~ (uutils) concatenate and display input"
|
||||||
|
@ -17,7 +17,6 @@ path = "src/cat.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
is-terminal = { workspace = true }
|
|
||||||
uucore = { workspace = true, features = ["fs", "pipes"] }
|
uucore = { workspace = true, features = ["fs", "pipes"] }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
// 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) Evgeniy Klyuchikov <evgeniy.klyuchikov@gmail.com>
|
|
||||||
// (c) Joshua S. Miller <jsmiller@uchicago.edu>
|
|
||||||
// (c) Árni Dagur <arni@dagur.eu>
|
|
||||||
//
|
|
||||||
// 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.
|
||||||
|
|
||||||
|
@ -12,9 +7,8 @@
|
||||||
|
|
||||||
// last synced with: cat (GNU coreutils) 8.13
|
// last synced with: cat (GNU coreutils) 8.13
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use is_terminal::IsTerminal;
|
|
||||||
use std::fs::{metadata, File};
|
use std::fs::{metadata, File};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, IsTerminal, Read, Write};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::UResult;
|
use uucore::error::UResult;
|
||||||
|
@ -192,7 +186,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
NumberingMode::None
|
NumberingMode::None
|
||||||
};
|
};
|
||||||
|
|
||||||
let show_nonprint = vec![
|
let show_nonprint = [
|
||||||
options::SHOW_ALL.to_owned(),
|
options::SHOW_ALL.to_owned(),
|
||||||
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
||||||
options::SHOW_NONPRINTING_TABS.to_owned(),
|
options::SHOW_NONPRINTING_TABS.to_owned(),
|
||||||
|
@ -201,7 +195,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.iter()
|
.iter()
|
||||||
.any(|v| matches.get_flag(v));
|
.any(|v| matches.get_flag(v));
|
||||||
|
|
||||||
let show_ends = vec![
|
let show_ends = [
|
||||||
options::SHOW_ENDS.to_owned(),
|
options::SHOW_ENDS.to_owned(),
|
||||||
options::SHOW_ALL.to_owned(),
|
options::SHOW_ALL.to_owned(),
|
||||||
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
options::SHOW_NONPRINTING_ENDS.to_owned(),
|
||||||
|
@ -209,7 +203,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.iter()
|
.iter()
|
||||||
.any(|v| matches.get_flag(v));
|
.any(|v| matches.get_flag(v));
|
||||||
|
|
||||||
let show_tabs = vec![
|
let show_tabs = [
|
||||||
options::SHOW_ALL.to_owned(),
|
options::SHOW_ALL.to_owned(),
|
||||||
options::SHOW_TABS.to_owned(),
|
options::SHOW_TABS.to_owned(),
|
||||||
options::SHOW_NONPRINTING_TABS.to_owned(),
|
options::SHOW_NONPRINTING_TABS.to_owned(),
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
use super::{CatResult, FdReadable, InputHandle};
|
use super::{CatResult, FdReadable, InputHandle};
|
||||||
|
|
||||||
use nix::unistd;
|
use nix::unistd;
|
||||||
|
@ -17,7 +21,7 @@ const BUF_SIZE: usize = 1024 * 16;
|
||||||
/// copying or not. False means we don't have to.
|
/// copying or not. False means we don't have to.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn write_fast_using_splice<R: FdReadable>(
|
pub(super) fn write_fast_using_splice<R: FdReadable>(
|
||||||
handle: &mut InputHandle<R>,
|
handle: &InputHandle<R>,
|
||||||
write_fd: &impl AsRawFd,
|
write_fd: &impl AsRawFd,
|
||||||
) -> CatResult<bool> {
|
) -> CatResult<bool> {
|
||||||
let (pipe_rd, pipe_wr) = pipe()?;
|
let (pipe_rd, pipe_wr) = pipe()?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chcon"
|
name = "uu_chcon"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chcon ~ (uutils) change file security context"
|
description = "chcon ~ (uutils) change file security context"
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore (vars) RFILE
|
// spell-checker:ignore (vars) RFILE
|
||||||
#![allow(clippy::upper_case_acronyms)]
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
use std::ffi::{CStr, CString, OsStr};
|
use std::ffi::{CStr, CString, OsStr};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::{c_int, c_long, c_short};
|
use std::os::raw::{c_int, c_long, c_short};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chgrp"
|
name = "uu_chgrp"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chgrp ~ (uutils) change the group ownership of FILE"
|
description = "chgrp ~ (uutils) change the group ownership of FILE"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Jian Zeng <anonymousknight96@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.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chmod"
|
name = "uu_chmod"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chmod ~ (uutils) change mode of FILE"
|
description = "chmod ~ (uutils) change mode of FILE"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Alex Lyon <arcterus@mail.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.
|
||||||
|
|
||||||
|
@ -337,9 +335,7 @@ impl Chmoder {
|
||||||
let mut new_mode = fperm;
|
let mut new_mode = fperm;
|
||||||
let mut naively_expected_new_mode = new_mode;
|
let mut naively_expected_new_mode = new_mode;
|
||||||
for mode in cmode_unwrapped.split(',') {
|
for mode in cmode_unwrapped.split(',') {
|
||||||
// cmode is guaranteed to be Some in this case
|
let result = if mode.chars().any(|c| c.is_ascii_digit()) {
|
||||||
let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
||||||
let result = if mode.contains(arr) {
|
|
||||||
mode::parse_numeric(new_mode, mode, file.is_dir()).map(|v| (v, v))
|
mode::parse_numeric(new_mode, mode, file.is_dir()).map(|v| (v, v))
|
||||||
} else {
|
} else {
|
||||||
mode::parse_symbolic(new_mode, mode, get_umask(), file.is_dir()).map(|m| {
|
mode::parse_symbolic(new_mode, mode, get_umask(), file.is_dir()).map(|m| {
|
||||||
|
@ -354,20 +350,22 @@ impl Chmoder {
|
||||||
(m, naive_mode)
|
(m, naive_mode)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok((mode, naive_mode)) => {
|
Ok((mode, naive_mode)) => {
|
||||||
new_mode = mode;
|
new_mode = mode;
|
||||||
naively_expected_new_mode = naive_mode;
|
naively_expected_new_mode = naive_mode;
|
||||||
}
|
}
|
||||||
Err(f) => {
|
Err(f) => {
|
||||||
if self.quiet {
|
return if self.quiet {
|
||||||
return Err(ExitCode::new(1));
|
Err(ExitCode::new(1))
|
||||||
} else {
|
} else {
|
||||||
return Err(USimpleError::new(1, f));
|
Err(USimpleError::new(1, f))
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -438,25 +436,25 @@ mod tests {
|
||||||
fn test_extract_negative_modes() {
|
fn test_extract_negative_modes() {
|
||||||
// "chmod -w -r file" becomes "chmod -w,-r file". clap does not accept "-w,-r" as MODE.
|
// "chmod -w -r file" becomes "chmod -w,-r file". clap does not accept "-w,-r" as MODE.
|
||||||
// Therefore, "w" is added as pseudo mode to pass clap.
|
// Therefore, "w" is added as pseudo mode to pass clap.
|
||||||
let (c, a) = extract_negative_modes(vec!["-w", "-r", "file"].iter().map(OsString::from));
|
let (c, a) = extract_negative_modes(["-w", "-r", "file"].iter().map(OsString::from));
|
||||||
assert_eq!(c, Some("-w,-r".to_string()));
|
assert_eq!(c, Some("-w,-r".to_string()));
|
||||||
assert_eq!(a, vec!["w", "file"]);
|
assert_eq!(a, ["w", "file"]);
|
||||||
|
|
||||||
// "chmod -w file -r" becomes "chmod -w,-r file". clap does not accept "-w,-r" as MODE.
|
// "chmod -w file -r" becomes "chmod -w,-r file". clap does not accept "-w,-r" as MODE.
|
||||||
// Therefore, "w" is added as pseudo mode to pass clap.
|
// Therefore, "w" is added as pseudo mode to pass clap.
|
||||||
let (c, a) = extract_negative_modes(vec!["-w", "file", "-r"].iter().map(OsString::from));
|
let (c, a) = extract_negative_modes(["-w", "file", "-r"].iter().map(OsString::from));
|
||||||
assert_eq!(c, Some("-w,-r".to_string()));
|
assert_eq!(c, Some("-w,-r".to_string()));
|
||||||
assert_eq!(a, vec!["w", "file"]);
|
assert_eq!(a, ["w", "file"]);
|
||||||
|
|
||||||
// "chmod -w -- -r file" becomes "chmod -w -r file", where "-r" is interpreted as file.
|
// "chmod -w -- -r file" becomes "chmod -w -r file", where "-r" is interpreted as file.
|
||||||
// Again, "w" is needed as pseudo mode.
|
// Again, "w" is needed as pseudo mode.
|
||||||
let (c, a) = extract_negative_modes(vec!["-w", "--", "-r", "f"].iter().map(OsString::from));
|
let (c, a) = extract_negative_modes(["-w", "--", "-r", "f"].iter().map(OsString::from));
|
||||||
assert_eq!(c, Some("-w".to_string()));
|
assert_eq!(c, Some("-w".to_string()));
|
||||||
assert_eq!(a, vec!["w", "--", "-r", "f"]);
|
assert_eq!(a, ["w", "--", "-r", "f"]);
|
||||||
|
|
||||||
// "chmod -- -r file" becomes "chmod -r file".
|
// "chmod -- -r file" becomes "chmod -r file".
|
||||||
let (c, a) = extract_negative_modes(vec!["--", "-r", "file"].iter().map(OsString::from));
|
let (c, a) = extract_negative_modes(["--", "-r", "file"].iter().map(OsString::from));
|
||||||
assert_eq!(c, None);
|
assert_eq!(c, None);
|
||||||
assert_eq!(a, vec!["--", "-r", "file"]);
|
assert_eq!(a, ["--", "-r", "file"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chown"
|
name = "uu_chown"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chown ~ (uutils) change the ownership of FILE"
|
description = "chown ~ (uutils) change the ownership of FILE"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Jian Zeng <anonymousknight96@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.
|
||||||
|
|
||||||
|
@ -197,6 +195,53 @@ pub fn uu_app() -> Command {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses the user string to extract the UID.
|
||||||
|
fn parse_uid(user: &str, spec: &str, sep: char) -> UResult<Option<u32>> {
|
||||||
|
if user.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
match Passwd::locate(user) {
|
||||||
|
Ok(u) => Ok(Some(u.uid)), // We have been able to get the uid
|
||||||
|
Err(_) => {
|
||||||
|
// we have NOT been able to find the uid
|
||||||
|
// but we could be in the case where we have user.group
|
||||||
|
if spec.contains('.') && !spec.contains(':') && sep == ':' {
|
||||||
|
// but the input contains a '.' but not a ':'
|
||||||
|
// we might have something like username.groupname
|
||||||
|
// So, try to parse it this way
|
||||||
|
parse_spec(spec, '.').map(|(uid, _)| uid)
|
||||||
|
} else {
|
||||||
|
// It's possible that the `user` string contains a
|
||||||
|
// numeric user ID, in which case, we respect that.
|
||||||
|
match user.parse() {
|
||||||
|
Ok(uid) => Ok(Some(uid)),
|
||||||
|
Err(_) => Err(USimpleError::new(
|
||||||
|
1,
|
||||||
|
format!("invalid user: {}", spec.quote()),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the group string to extract the GID.
|
||||||
|
fn parse_gid(group: &str, spec: &str) -> UResult<Option<u32>> {
|
||||||
|
if group.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
match Group::locate(group) {
|
||||||
|
Ok(g) => Ok(Some(g.gid)),
|
||||||
|
Err(_) => match group.parse() {
|
||||||
|
Ok(gid) => Ok(Some(gid)),
|
||||||
|
Err(_) => Err(USimpleError::new(
|
||||||
|
1,
|
||||||
|
format!("invalid group: {}", spec.quote()),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse the owner/group specifier string into a user ID and a group ID.
|
/// Parse the owner/group specifier string into a user ID and a group ID.
|
||||||
///
|
///
|
||||||
/// The `spec` can be of the form:
|
/// The `spec` can be of the form:
|
||||||
|
@ -215,52 +260,8 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
|
||||||
let user = args.next().unwrap_or("");
|
let user = args.next().unwrap_or("");
|
||||||
let group = args.next().unwrap_or("");
|
let group = args.next().unwrap_or("");
|
||||||
|
|
||||||
let uid = if user.is_empty() {
|
let uid = parse_uid(user, spec, sep)?;
|
||||||
None
|
let gid = parse_gid(group, spec)?;
|
||||||
} else {
|
|
||||||
Some(match Passwd::locate(user) {
|
|
||||||
Ok(u) => u.uid, // We have been able to get the uid
|
|
||||||
Err(_) =>
|
|
||||||
// we have NOT been able to find the uid
|
|
||||||
// but we could be in the case where we have user.group
|
|
||||||
{
|
|
||||||
if spec.contains('.') && !spec.contains(':') && sep == ':' {
|
|
||||||
// but the input contains a '.' but not a ':'
|
|
||||||
// we might have something like username.groupname
|
|
||||||
// So, try to parse it this way
|
|
||||||
return parse_spec(spec, '.');
|
|
||||||
} else {
|
|
||||||
// It's possible that the `user` string contains a
|
|
||||||
// numeric user ID, in which case, we respect that.
|
|
||||||
match user.parse() {
|
|
||||||
Ok(uid) => uid,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(USimpleError::new(
|
|
||||||
1,
|
|
||||||
format!("invalid user: {}", spec.quote()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let gid = if group.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(match Group::locate(group) {
|
|
||||||
Ok(g) => g.gid,
|
|
||||||
Err(_) => match group.parse() {
|
|
||||||
Ok(gid) => gid,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(USimpleError::new(
|
|
||||||
1,
|
|
||||||
format!("invalid group: {}", spec.quote()),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
if user.chars().next().map(char::is_numeric).unwrap_or(false)
|
if user.chars().next().map(char::is_numeric).unwrap_or(false)
|
||||||
&& group.is_empty()
|
&& group.is_empty()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_chroot"
|
name = "uu_chroot"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "chroot ~ (uutils) run COMMAND under a new root directory"
|
description = "chroot ~ (uutils) run COMMAND under a new root directory"
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Vsevolod Velichko <torkvemada@sorokdva.net>
|
|
||||||
// (c) Jian Zeng <anonymousknight96 AT 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.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 NEWROOT Userspec userspec
|
// spell-checker:ignore NEWROOT Userspec userspec
|
||||||
//! Errors returned by chroot.
|
//! Errors returned by chroot.
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cksum"
|
name = "uu_cksum"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "cksum ~ (uutils) display CRC and size of input"
|
description = "cksum ~ (uutils) display CRC and size of input"
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Michael Gehring <mg@ebfe.org>
|
// 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) fname, algo
|
// spell-checker:ignore (ToDO) fname, algo
|
||||||
|
@ -209,8 +207,7 @@ fn digest_read<T: Read>(
|
||||||
Ok((digest.result_str(), output_size))
|
Ok((digest.result_str(), output_size))
|
||||||
} else {
|
} else {
|
||||||
// Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016)
|
// Assume it's SHAKE. result_str() doesn't work with shake (as of 8/30/2016)
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = vec![0; (output_bits + 7) / 8];
|
||||||
bytes.resize((output_bits + 7) / 8, 0);
|
|
||||||
digest.hash_finalize(&mut bytes);
|
digest.hash_finalize(&mut bytes);
|
||||||
Ok((encode(bytes), output_size))
|
Ok((encode(bytes), output_size))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_comm"
|
name = "uu_comm"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "comm ~ (uutils) compare sorted inputs"
|
description = "comm ~ (uutils) compare sorted inputs"
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Michael Gehring <mg@ebfe.org>
|
|
||||||
//
|
|
||||||
// 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) delim mkdelim
|
// spell-checker:ignore (ToDO) delim mkdelim
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, stdin, BufRead, BufReader, Stdin};
|
use std::io::{self, stdin, BufRead, BufReader, Stdin};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::error::{FromIo, UResult};
|
use uucore::error::{FromIo, UResult};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||||
|
@ -32,46 +30,6 @@ mod options {
|
||||||
pub const ZERO_TERMINATED: &str = "zero-terminated";
|
pub const ZERO_TERMINATED: &str = "zero-terminated";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn column_width(col: &str, opts: &ArgMatches) -> usize {
|
|
||||||
if opts.get_flag(col) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum LineEnding {
|
|
||||||
Newline = b'\n',
|
|
||||||
Nul = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LineEnding> for u8 {
|
|
||||||
fn from(line_ending: LineEnding) -> Self {
|
|
||||||
line_ending as Self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for LineEnding {
|
|
||||||
fn from(is_zero_terminated: bool) -> Self {
|
|
||||||
if is_zero_terminated {
|
|
||||||
Self::Nul
|
|
||||||
} else {
|
|
||||||
Self::Newline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for LineEnding {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Newline => writeln!(f),
|
|
||||||
Self::Nul => write!(f, "\0"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Input {
|
enum Input {
|
||||||
Stdin(Stdin),
|
Stdin(Stdin),
|
||||||
FileIn(BufReader<File>),
|
FileIn(BufReader<File>),
|
||||||
|
@ -109,8 +67,8 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||||
delim => delim,
|
delim => delim,
|
||||||
};
|
};
|
||||||
|
|
||||||
let width_col_1 = column_width(options::COLUMN_1, opts);
|
let width_col_1 = usize::from(!opts.get_flag(options::COLUMN_1));
|
||||||
let width_col_2 = column_width(options::COLUMN_2, opts);
|
let width_col_2 = usize::from(!opts.get_flag(options::COLUMN_2));
|
||||||
|
|
||||||
let delim_col_2 = delim.repeat(width_col_1);
|
let delim_col_2 = delim.repeat(width_col_1);
|
||||||
let delim_col_3 = delim.repeat(width_col_1 + width_col_2);
|
let delim_col_3 = delim.repeat(width_col_1 + width_col_2);
|
||||||
|
@ -168,7 +126,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &ArgMatches) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.get_flag(options::TOTAL) {
|
if opts.get_flag(options::TOTAL) {
|
||||||
let line_ending = LineEnding::from(opts.get_flag(options::ZERO_TERMINATED));
|
let line_ending = LineEnding::from_zero_flag(opts.get_flag(options::ZERO_TERMINATED));
|
||||||
print!("{total_col_1}{delim}{total_col_2}{delim}{total_col_3}{delim}total{line_ending}");
|
print!("{total_col_1}{delim}{total_col_2}{delim}{total_col_3}{delim}total{line_ending}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +148,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let args = args.collect_lossy();
|
let args = args.collect_lossy();
|
||||||
|
|
||||||
let matches = uu_app().try_get_matches_from(args)?;
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
let line_ending = LineEnding::from(matches.get_flag(options::ZERO_TERMINATED));
|
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED));
|
||||||
let filename1 = matches.get_one::<String>(options::FILE_1).unwrap();
|
let filename1 = matches.get_one::<String>(options::FILE_1).unwrap();
|
||||||
let filename2 = matches.get_one::<String>(options::FILE_2).unwrap();
|
let filename2 = matches.get_one::<String>(options::FILE_2).unwrap();
|
||||||
let mut f1 = open_file(filename1, line_ending).map_err_context(|| filename1.to_string())?;
|
let mut f1 = open_file(filename1, line_ending).map_err_context(|| filename1.to_string())?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cp"
|
name = "uu_cp"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
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,7 +24,14 @@ filetime = { workspace = true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
quick-error = { workspace = true }
|
quick-error = { workspace = true }
|
||||||
selinux = { workspace = true, optional = true }
|
selinux = { workspace = true, optional = true }
|
||||||
uucore = { workspace = true, features = ["entries", "fs", "perms", "mode"] }
|
uucore = { workspace = true, features = [
|
||||||
|
"backup-control",
|
||||||
|
"entries",
|
||||||
|
"fs",
|
||||||
|
"perms",
|
||||||
|
"mode",
|
||||||
|
"update-control",
|
||||||
|
] }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
indicatif = { workspace = true }
|
indicatif = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 canonicalizes direntry pathbuf symlinked
|
// spell-checker:ignore TODO canonicalizes direntry pathbuf symlinked
|
||||||
//! Recursively copy the contents of a directory.
|
//! Recursively copy the contents of a directory.
|
||||||
//!
|
//!
|
||||||
//! See the [`copy_directory`] function for more information.
|
//! See the [`copy_directory`] function for more information.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -24,8 +24,8 @@ use uucore::uio_error;
|
||||||
use walkdir::{DirEntry, WalkDir};
|
use walkdir::{DirEntry, WalkDir};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
aligned_ancestors, context_for, copy_attributes, copy_file, copy_link, preserve_hardlinks,
|
aligned_ancestors, context_for, copy_attributes, copy_file, copy_link, CopyResult, Error,
|
||||||
CopyResult, Error, Options, TargetSlice,
|
Options,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Ensure a Windows path starts with a `\\?`.
|
/// Ensure a Windows path starts with a `\\?`.
|
||||||
|
@ -33,8 +33,8 @@ use crate::{
|
||||||
fn adjust_canonicalization(p: &Path) -> Cow<Path> {
|
fn adjust_canonicalization(p: &Path) -> Cow<Path> {
|
||||||
// In some cases, \\? can be missing on some Windows paths. Add it at the
|
// In some cases, \\? can be missing on some Windows paths. Add it at the
|
||||||
// beginning unless the path is prefixed with a device namespace.
|
// beginning unless the path is prefixed with a device namespace.
|
||||||
const VERBATIM_PREFIX: &str = r#"\\?"#;
|
const VERBATIM_PREFIX: &str = r"\\?";
|
||||||
const DEVICE_NS_PREFIX: &str = r#"\\."#;
|
const DEVICE_NS_PREFIX: &str = r"\\.";
|
||||||
|
|
||||||
let has_prefix = p
|
let has_prefix = p
|
||||||
.components()
|
.components()
|
||||||
|
@ -200,7 +200,7 @@ fn copy_direntry(
|
||||||
options: &Options,
|
options: &Options,
|
||||||
symlinked_files: &mut HashSet<FileInformation>,
|
symlinked_files: &mut HashSet<FileInformation>,
|
||||||
preserve_hard_links: bool,
|
preserve_hard_links: bool,
|
||||||
hard_links: &mut Vec<(String, u64)>,
|
copied_files: &mut HashMap<FileInformation, PathBuf>,
|
||||||
) -> CopyResult<()> {
|
) -> CopyResult<()> {
|
||||||
let Entry {
|
let Entry {
|
||||||
source_absolute,
|
source_absolute,
|
||||||
|
@ -240,30 +240,27 @@ fn copy_direntry(
|
||||||
// If the source is not a directory, then we need to copy the file.
|
// If the source is not a directory, then we need to copy the file.
|
||||||
if !source_absolute.is_dir() {
|
if !source_absolute.is_dir() {
|
||||||
if preserve_hard_links {
|
if preserve_hard_links {
|
||||||
let dest = local_to_target.as_path().to_path_buf();
|
match copy_file(
|
||||||
let found_hard_link = preserve_hardlinks(hard_links, &source_absolute, &dest)?;
|
progress_bar,
|
||||||
if !found_hard_link {
|
&source_absolute,
|
||||||
match copy_file(
|
local_to_target.as_path(),
|
||||||
progress_bar,
|
options,
|
||||||
&source_absolute,
|
symlinked_files,
|
||||||
local_to_target.as_path(),
|
copied_files,
|
||||||
options,
|
false,
|
||||||
symlinked_files,
|
) {
|
||||||
false,
|
Ok(_) => Ok(()),
|
||||||
) {
|
Err(err) => {
|
||||||
Ok(_) => Ok(()),
|
if source_absolute.is_symlink() {
|
||||||
Err(err) => {
|
// silent the error with a symlink
|
||||||
if source_absolute.is_symlink() {
|
// In case we do --archive, we might copy the symlink
|
||||||
// silent the error with a symlink
|
// before the file itself
|
||||||
// In case we do --archive, we might copy the symlink
|
Ok(())
|
||||||
// before the file itself
|
} else {
|
||||||
Ok(())
|
Err(err)
|
||||||
} else {
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}?;
|
}
|
||||||
}
|
}?;
|
||||||
} else {
|
} else {
|
||||||
// At this point, `path` is just a plain old file.
|
// At this point, `path` is just a plain old file.
|
||||||
// Terminate this function immediately if there is any
|
// Terminate this function immediately if there is any
|
||||||
|
@ -277,6 +274,7 @@ fn copy_direntry(
|
||||||
local_to_target.as_path(),
|
local_to_target.as_path(),
|
||||||
options,
|
options,
|
||||||
symlinked_files,
|
symlinked_files,
|
||||||
|
copied_files,
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
@ -307,9 +305,10 @@ fn copy_direntry(
|
||||||
pub(crate) fn copy_directory(
|
pub(crate) fn copy_directory(
|
||||||
progress_bar: &Option<ProgressBar>,
|
progress_bar: &Option<ProgressBar>,
|
||||||
root: &Path,
|
root: &Path,
|
||||||
target: &TargetSlice,
|
target: &Path,
|
||||||
options: &Options,
|
options: &Options,
|
||||||
symlinked_files: &mut HashSet<FileInformation>,
|
symlinked_files: &mut HashSet<FileInformation>,
|
||||||
|
copied_files: &mut HashMap<FileInformation, PathBuf>,
|
||||||
source_in_command_line: bool,
|
source_in_command_line: bool,
|
||||||
) -> CopyResult<()> {
|
) -> CopyResult<()> {
|
||||||
if !options.recursive {
|
if !options.recursive {
|
||||||
|
@ -324,6 +323,7 @@ pub(crate) fn copy_directory(
|
||||||
target,
|
target,
|
||||||
options,
|
options,
|
||||||
symlinked_files,
|
symlinked_files,
|
||||||
|
copied_files,
|
||||||
source_in_command_line,
|
source_in_command_line,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -372,7 +372,6 @@ pub(crate) fn copy_directory(
|
||||||
};
|
};
|
||||||
let target = tmp.as_path();
|
let target = tmp.as_path();
|
||||||
|
|
||||||
let mut hard_links: Vec<(String, u64)> = vec![];
|
|
||||||
let preserve_hard_links = options.preserve_hard_links();
|
let preserve_hard_links = options.preserve_hard_links();
|
||||||
|
|
||||||
// Collect some paths here that are invariant during the traversal
|
// Collect some paths here that are invariant during the traversal
|
||||||
|
@ -397,7 +396,7 @@ pub(crate) fn copy_directory(
|
||||||
options,
|
options,
|
||||||
symlinked_files,
|
symlinked_files,
|
||||||
preserve_hard_links,
|
preserve_hard_links,
|
||||||
&mut hard_links,
|
copied_files,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
// Print an error message, but continue traversing the directory.
|
// Print an error message, but continue traversing the directory.
|
||||||
|
@ -448,6 +447,7 @@ mod tests {
|
||||||
use super::ends_with_slash_dot;
|
use super::ends_with_slash_dot;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn test_ends_with_slash_dot() {
|
fn test_ends_with_slash_dot() {
|
||||||
assert!(ends_with_slash_dot("/."));
|
assert!(ends_with_slash_dot("/."));
|
||||||
assert!(ends_with_slash_dot("./."));
|
assert!(ends_with_slash_dot("./."));
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
// spell-checker:ignore (ToDO) copydir ficlone fiemap ftruncate linkgs lstat nlink nlinks pathbuf pwrite reflink strs xattrs symlinked deduplicated advcpmv nushell IRWXG IRWXO IRWXU IRWXUGO IRWXU IRWXG IRWXO IRWXUGO
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
#![allow(clippy::extra_unused_lifetimes)]
|
#![allow(clippy::extra_unused_lifetimes)]
|
||||||
|
|
||||||
// This file is part of the uutils coreutils package.
|
|
||||||
//
|
|
||||||
// (c) Jordy Dickinson <jordy.dickinson@gmail.com>
|
|
||||||
// (c) Joshua S. Miller <jsmiller@uchicago.edu>
|
|
||||||
//
|
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
|
||||||
// that was distributed with this source code.
|
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) copydir ficlone fiemap ftruncate linkgs lstat nlink nlinks pathbuf pwrite reflink strs xattrs symlinked deduplicated advcpmv
|
|
||||||
|
|
||||||
use quick_error::quick_error;
|
use quick_error::quick_error;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashSet;
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::env;
|
use std::env;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
@ -34,14 +30,16 @@ use libc::mkfifo;
|
||||||
use quick_error::ResultExt;
|
use quick_error::ResultExt;
|
||||||
|
|
||||||
use platform::copy_on_write;
|
use platform::copy_on_write;
|
||||||
use uucore::backup_control::{self, BackupMode};
|
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{set_exit_code, UClapError, UError, UResult, UUsageError};
|
use uucore::error::{set_exit_code, UClapError, UError, UResult, UUsageError};
|
||||||
use uucore::fs::{
|
use uucore::fs::{
|
||||||
canonicalize, is_symlink_loop, paths_refer_to_same_file, FileInformation, MissingHandling,
|
canonicalize, is_symlink_loop, paths_refer_to_same_file, FileInformation, MissingHandling,
|
||||||
ResolveMode,
|
ResolveMode,
|
||||||
};
|
};
|
||||||
use uucore::update_control::{self, UpdateMode};
|
use uucore::{backup_control, update_control};
|
||||||
|
// These are exposed for projects (e.g. nushell) that want to create an `Options` value, which
|
||||||
|
// requires these enum.
|
||||||
|
pub use uucore::{backup_control::BackupMode, update_control::UpdateMode};
|
||||||
use uucore::{
|
use uucore::{
|
||||||
crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error,
|
crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error,
|
||||||
show_warning, util_name,
|
show_warning, util_name,
|
||||||
|
@ -51,6 +49,7 @@ use crate::copydir::copy_directory;
|
||||||
|
|
||||||
mod copydir;
|
mod copydir;
|
||||||
mod platform;
|
mod platform;
|
||||||
|
|
||||||
quick_error! {
|
quick_error! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -108,12 +107,8 @@ impl UError for Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CopyResult<T> = Result<T, Error>;
|
pub type CopyResult<T> = Result<T, Error>;
|
||||||
pub type Source = PathBuf;
|
|
||||||
pub type SourceSlice = Path;
|
|
||||||
pub type Target = PathBuf;
|
|
||||||
pub type TargetSlice = Path;
|
|
||||||
|
|
||||||
/// Specifies whether when overwrite files
|
/// Specifies how to overwrite files.
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum ClobberMode {
|
pub enum ClobberMode {
|
||||||
Force,
|
Force,
|
||||||
|
@ -121,7 +116,7 @@ pub enum ClobberMode {
|
||||||
Standard,
|
Standard,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies whether when overwrite files
|
/// Specifies whether files should be overwritten.
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum OverwriteMode {
|
pub enum OverwriteMode {
|
||||||
/// [Default] Always overwrite existing files
|
/// [Default] Always overwrite existing files
|
||||||
|
@ -148,12 +143,13 @@ pub enum SparseMode {
|
||||||
Never,
|
Never,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies the expected file type of copy target
|
/// The expected file type of copy target
|
||||||
pub enum TargetType {
|
pub enum TargetType {
|
||||||
Directory,
|
Directory,
|
||||||
File,
|
File,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copy action to perform
|
||||||
pub enum CopyMode {
|
pub enum CopyMode {
|
||||||
Link,
|
Link,
|
||||||
SymLink,
|
SymLink,
|
||||||
|
@ -162,77 +158,130 @@ pub enum CopyMode {
|
||||||
AttrOnly,
|
AttrOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Preservation settings for various attributes
|
||||||
|
///
|
||||||
|
/// It should be derived from options as follows:
|
||||||
|
///
|
||||||
|
/// - if there is a list of attributes to preserve (i.e. `--preserve=ATTR_LIST`) parse that list with [`Attributes::parse_iter`],
|
||||||
|
/// - if `-p` or `--preserve` is given without arguments, use [`Attributes::DEFAULT`],
|
||||||
|
/// - if `-a`/`--archive` is passed, use [`Attributes::ALL`],
|
||||||
|
/// - if `-d` is passed use [`Attributes::LINKS`],
|
||||||
|
/// - otherwise, use [`Attributes::NONE`].
|
||||||
|
///
|
||||||
|
/// For full compatibility with GNU, these options should also combine. We
|
||||||
|
/// currently only do a best effort imitation of that behavior, because it is
|
||||||
|
/// difficult to achieve in clap, especially with `--no-preserve`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Attributes {
|
pub struct Attributes {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ownership: Preserve,
|
pub ownership: Preserve,
|
||||||
mode: Preserve,
|
pub mode: Preserve,
|
||||||
timestamps: Preserve,
|
pub timestamps: Preserve,
|
||||||
context: Preserve,
|
pub context: Preserve,
|
||||||
links: Preserve,
|
pub links: Preserve,
|
||||||
xattr: Preserve,
|
pub xattr: Preserve,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes {
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub(crate) fn max(&mut self, other: Self) {
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
self.ownership = self.ownership.max(other.ownership);
|
|
||||||
}
|
|
||||||
self.mode = self.mode.max(other.mode);
|
|
||||||
self.timestamps = self.timestamps.max(other.timestamps);
|
|
||||||
self.context = self.context.max(other.context);
|
|
||||||
self.links = self.links.max(other.links);
|
|
||||||
self.xattr = self.xattr.max(other.xattr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum Preserve {
|
pub enum Preserve {
|
||||||
No,
|
// explicit means whether the --no-preserve flag is used or not to distinguish out the default value.
|
||||||
|
// e.g. --no-preserve=mode means mode = No { explicit = true }
|
||||||
|
No { explicit: bool },
|
||||||
Yes { required: bool },
|
Yes { required: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Preserve {
|
impl PartialOrd for Preserve {
|
||||||
/// Preservation level should only increase, with no preservation being the lowest option,
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
/// preserve but don't require - middle, and preserve and require - top.
|
Some(self.cmp(other))
|
||||||
pub(crate) fn max(&self, other: Self) -> Self {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Preserve {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Yes { required: true }, _) | (_, Self::Yes { required: true }) => {
|
(Self::No { .. }, Self::No { .. }) => Ordering::Equal,
|
||||||
Self::Yes { required: true }
|
(Self::Yes { .. }, Self::No { .. }) => Ordering::Greater,
|
||||||
}
|
(Self::No { .. }, Self::Yes { .. }) => Ordering::Less,
|
||||||
(Self::Yes { required: false }, _) | (_, Self::Yes { required: false }) => {
|
(
|
||||||
Self::Yes { required: false }
|
Self::Yes { required: req_self },
|
||||||
}
|
Self::Yes {
|
||||||
_ => Self::No,
|
required: req_other,
|
||||||
|
},
|
||||||
|
) => req_self.cmp(req_other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-usable, extensible copy options
|
/// Options for the `cp` command
|
||||||
|
///
|
||||||
|
/// All options are public so that the options can be programmatically
|
||||||
|
/// constructed by other crates, such as nushell. That means that this struct
|
||||||
|
/// is part of our public API. It should therefore not be changed without good
|
||||||
|
/// reason.
|
||||||
|
///
|
||||||
|
/// The fields are documented with the arguments that determine their value.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
attributes_only: bool,
|
/// `--attributes-only`
|
||||||
backup: BackupMode,
|
pub attributes_only: bool,
|
||||||
copy_contents: bool,
|
/// `--backup[=CONTROL]`, `-b`
|
||||||
cli_dereference: bool,
|
pub backup: BackupMode,
|
||||||
copy_mode: CopyMode,
|
/// `--copy-contents`
|
||||||
dereference: bool,
|
pub copy_contents: bool,
|
||||||
no_target_dir: bool,
|
/// `-H`
|
||||||
one_file_system: bool,
|
pub cli_dereference: bool,
|
||||||
overwrite: OverwriteMode,
|
/// Determines the type of copying that should be done
|
||||||
parents: bool,
|
///
|
||||||
sparse_mode: SparseMode,
|
/// Set by the following arguments:
|
||||||
strip_trailing_slashes: bool,
|
/// - `-l`, `--link`: [`CopyMode::Link`]
|
||||||
reflink_mode: ReflinkMode,
|
/// - `-s`, `--symbolic-link`: [`CopyMode::SymLink`]
|
||||||
attributes: Attributes,
|
/// - `-u`, `--update[=WHEN]`: [`CopyMode::Update`]
|
||||||
recursive: bool,
|
/// - `--attributes-only`: [`CopyMode::AttrOnly`]
|
||||||
backup_suffix: String,
|
/// - otherwise: [`CopyMode::Copy`]
|
||||||
target_dir: Option<PathBuf>,
|
pub copy_mode: CopyMode,
|
||||||
update: UpdateMode,
|
/// `-L`, `--dereference`
|
||||||
debug: bool,
|
pub dereference: bool,
|
||||||
verbose: bool,
|
/// `-T`, `--no-target-dir`
|
||||||
progress_bar: bool,
|
pub no_target_dir: bool,
|
||||||
|
/// `-x`, `--one-file-system`
|
||||||
|
pub one_file_system: bool,
|
||||||
|
/// Specifies what to do with an existing destination
|
||||||
|
///
|
||||||
|
/// Set by the following arguments:
|
||||||
|
/// - `-i`, `--interactive`: [`OverwriteMode::Interactive`]
|
||||||
|
/// - `-n`, `--no-clobber`: [`OverwriteMode::NoClobber`]
|
||||||
|
/// - otherwise: [`OverwriteMode::Clobber`]
|
||||||
|
///
|
||||||
|
/// The `Interactive` and `Clobber` variants have a [`ClobberMode`] argument,
|
||||||
|
/// set by the following arguments:
|
||||||
|
/// - `-f`, `--force`: [`ClobberMode::Force`]
|
||||||
|
/// - `--remove-destination`: [`ClobberMode::RemoveDestination`]
|
||||||
|
/// - otherwise: [`ClobberMode::Standard`]
|
||||||
|
pub overwrite: OverwriteMode,
|
||||||
|
/// `--parents`
|
||||||
|
pub parents: bool,
|
||||||
|
/// `--sparse[=WHEN]`
|
||||||
|
pub sparse_mode: SparseMode,
|
||||||
|
/// `--strip-trailing-slashes`
|
||||||
|
pub strip_trailing_slashes: bool,
|
||||||
|
/// `--reflink[=WHEN]`
|
||||||
|
pub reflink_mode: ReflinkMode,
|
||||||
|
/// `--preserve=[=ATTRIBUTE_LIST]` and `--no-preserve=ATTRIBUTE_LIST`
|
||||||
|
pub attributes: Attributes,
|
||||||
|
/// `-R`, `-r`, `--recursive`
|
||||||
|
pub recursive: bool,
|
||||||
|
/// `-S`, `--suffix`
|
||||||
|
pub backup_suffix: String,
|
||||||
|
/// `-t`, `--target-directory`
|
||||||
|
pub target_dir: Option<PathBuf>,
|
||||||
|
/// `--update[=UPDATE]`
|
||||||
|
pub update: UpdateMode,
|
||||||
|
/// `--debug`
|
||||||
|
pub debug: bool,
|
||||||
|
/// `-v`, `--verbose`
|
||||||
|
pub verbose: bool,
|
||||||
|
/// `-g`, `--progress`
|
||||||
|
pub progress_bar: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum representing various debug states of the offload and reflink actions.
|
/// Enum representing various debug states of the offload and reflink actions.
|
||||||
|
@ -741,67 +790,90 @@ impl CopyMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes {
|
impl Attributes {
|
||||||
|
pub const ALL: Self = Self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
ownership: Preserve::Yes { required: true },
|
||||||
|
mode: Preserve::Yes { required: true },
|
||||||
|
timestamps: Preserve::Yes { required: true },
|
||||||
|
context: {
|
||||||
|
#[cfg(feature = "feat_selinux")]
|
||||||
|
{
|
||||||
|
Preserve::Yes { required: false }
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "feat_selinux"))]
|
||||||
|
{
|
||||||
|
Preserve::No { explicit: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
links: Preserve::Yes { required: true },
|
||||||
|
xattr: Preserve::Yes { required: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const NONE: Self = Self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
ownership: Preserve::No { explicit: false },
|
||||||
|
mode: Preserve::No { explicit: false },
|
||||||
|
timestamps: Preserve::No { explicit: false },
|
||||||
|
context: Preserve::No { explicit: false },
|
||||||
|
links: Preserve::No { explicit: false },
|
||||||
|
xattr: Preserve::No { explicit: false },
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: ownership is required if the user is root, for non-root users it's not required.
|
// TODO: ownership is required if the user is root, for non-root users it's not required.
|
||||||
// See: https://github.com/coreutils/coreutils/blob/master/src/copy.c#L3181
|
pub const DEFAULT: Self = Self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
ownership: Preserve::Yes { required: true },
|
||||||
|
mode: Preserve::Yes { required: true },
|
||||||
|
timestamps: Preserve::Yes { required: true },
|
||||||
|
..Self::NONE
|
||||||
|
};
|
||||||
|
|
||||||
fn all() -> Self {
|
pub const LINKS: Self = Self {
|
||||||
|
links: Preserve::Yes { required: true },
|
||||||
|
..Self::NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn union(self, other: &Self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ownership: Preserve::Yes { required: true },
|
ownership: self.ownership.max(other.ownership),
|
||||||
mode: Preserve::Yes { required: true },
|
context: self.context.max(other.context),
|
||||||
timestamps: Preserve::Yes { required: true },
|
timestamps: self.timestamps.max(other.timestamps),
|
||||||
context: {
|
mode: self.mode.max(other.mode),
|
||||||
#[cfg(feature = "feat_selinux")]
|
links: self.links.max(other.links),
|
||||||
{
|
xattr: self.xattr.max(other.xattr),
|
||||||
Preserve::Yes { required: false }
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "feat_selinux"))]
|
|
||||||
{
|
|
||||||
Preserve::No
|
|
||||||
}
|
|
||||||
},
|
|
||||||
links: Preserve::Yes { required: true },
|
|
||||||
xattr: Preserve::Yes { required: false },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default() -> Self {
|
pub fn parse_iter<T>(values: impl Iterator<Item = T>) -> Result<Self, Error>
|
||||||
Self {
|
where
|
||||||
#[cfg(unix)]
|
T: AsRef<str>,
|
||||||
ownership: Preserve::Yes { required: true },
|
{
|
||||||
mode: Preserve::Yes { required: true },
|
let mut new = Self::NONE;
|
||||||
timestamps: Preserve::Yes { required: true },
|
for value in values {
|
||||||
context: Preserve::No,
|
new = new.union(&Self::parse_single_string(value.as_ref())?);
|
||||||
links: Preserve::No,
|
|
||||||
xattr: Preserve::No,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn none() -> Self {
|
|
||||||
Self {
|
|
||||||
#[cfg(unix)]
|
|
||||||
ownership: Preserve::No,
|
|
||||||
mode: Preserve::No,
|
|
||||||
timestamps: Preserve::No,
|
|
||||||
context: Preserve::No,
|
|
||||||
links: Preserve::No,
|
|
||||||
xattr: Preserve::No,
|
|
||||||
}
|
}
|
||||||
|
Ok(new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to match string containing a parameter to preserve with the corresponding entry in the
|
/// Tries to match string containing a parameter to preserve with the corresponding entry in the
|
||||||
/// Attributes struct.
|
/// Attributes struct.
|
||||||
fn try_set_from_string(&mut self, value: &str) -> Result<(), Error> {
|
fn parse_single_string(value: &str) -> Result<Self, Error> {
|
||||||
let preserve_yes_required = Preserve::Yes { required: true };
|
let value = value.to_lowercase();
|
||||||
|
|
||||||
match &*value.to_lowercase() {
|
if value == "all" {
|
||||||
"mode" => self.mode = preserve_yes_required,
|
return Ok(Self::ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new = Self::NONE;
|
||||||
|
let attribute = match value.as_ref() {
|
||||||
|
"mode" => &mut new.mode,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
"ownership" => self.ownership = preserve_yes_required,
|
"ownership" => &mut new.ownership,
|
||||||
"timestamps" => self.timestamps = preserve_yes_required,
|
"timestamps" => &mut new.timestamps,
|
||||||
"context" => self.context = preserve_yes_required,
|
"context" => &mut new.context,
|
||||||
"link" | "links" => self.links = preserve_yes_required,
|
"link" | "links" => &mut new.links,
|
||||||
"xattr" => self.xattr = preserve_yes_required,
|
"xattr" => &mut new.xattr,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::InvalidArgument(format!(
|
return Err(Error::InvalidArgument(format!(
|
||||||
"invalid attribute {}",
|
"invalid attribute {}",
|
||||||
|
@ -809,7 +881,10 @@ impl Attributes {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
|
||||||
|
*attribute = Preserve::Yes { required: true };
|
||||||
|
|
||||||
|
Ok(new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,40 +933,35 @@ impl Options {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse attributes to preserve
|
// Parse attributes to preserve
|
||||||
let attributes: Attributes = if matches.contains_id(options::PRESERVE) {
|
let mut attributes =
|
||||||
match matches.get_many::<String>(options::PRESERVE) {
|
if let Some(attribute_strs) = matches.get_many::<String>(options::PRESERVE) {
|
||||||
None => Attributes::default(),
|
if attribute_strs.len() == 0 {
|
||||||
Some(attribute_strs) => {
|
Attributes::DEFAULT
|
||||||
let mut attributes: Attributes = Attributes::none();
|
} else {
|
||||||
let mut attributes_empty = true;
|
Attributes::parse_iter(attribute_strs)?
|
||||||
for attribute_str in attribute_strs {
|
}
|
||||||
attributes_empty = false;
|
} else if matches.get_flag(options::ARCHIVE) {
|
||||||
if attribute_str == "all" {
|
// --archive is used. Same as --preserve=all
|
||||||
attributes.max(Attributes::all());
|
Attributes::ALL
|
||||||
} else {
|
} else if matches.get_flag(options::NO_DEREFERENCE_PRESERVE_LINKS) {
|
||||||
attributes.try_set_from_string(attribute_str)?;
|
Attributes::LINKS
|
||||||
}
|
} else if matches.get_flag(options::PRESERVE_DEFAULT_ATTRIBUTES) {
|
||||||
}
|
Attributes::DEFAULT
|
||||||
// `--preserve` case, use the defaults
|
} else {
|
||||||
if attributes_empty {
|
Attributes::NONE
|
||||||
Attributes::default()
|
};
|
||||||
} else {
|
|
||||||
attributes
|
// handling no-preserve options and adjusting the attributes
|
||||||
}
|
if let Some(attribute_strs) = matches.get_many::<String>(options::NO_PRESERVE) {
|
||||||
|
if attribute_strs.len() > 0 {
|
||||||
|
let no_preserve_attributes = Attributes::parse_iter(attribute_strs)?;
|
||||||
|
if matches!(no_preserve_attributes.links, Preserve::Yes { .. }) {
|
||||||
|
attributes.links = Preserve::No { explicit: true };
|
||||||
|
} else if matches!(no_preserve_attributes.mode, Preserve::Yes { .. }) {
|
||||||
|
attributes.mode = Preserve::No { explicit: true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if matches.get_flag(options::ARCHIVE) {
|
}
|
||||||
// --archive is used. Same as --preserve=all
|
|
||||||
Attributes::all()
|
|
||||||
} else if matches.get_flag(options::NO_DEREFERENCE_PRESERVE_LINKS) {
|
|
||||||
let mut attributes = Attributes::none();
|
|
||||||
attributes.links = Preserve::Yes { required: true };
|
|
||||||
attributes
|
|
||||||
} else if matches.get_flag(options::PRESERVE_DEFAULT_ATTRIBUTES) {
|
|
||||||
Attributes::default()
|
|
||||||
} else {
|
|
||||||
Attributes::none()
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "feat_selinux"))]
|
#[cfg(not(feature = "feat_selinux"))]
|
||||||
if let Preserve::Yes { required } = attributes.context {
|
if let Preserve::Yes { required } = attributes.context {
|
||||||
|
@ -984,11 +1054,22 @@ impl Options {
|
||||||
|
|
||||||
fn preserve_hard_links(&self) -> bool {
|
fn preserve_hard_links(&self) -> bool {
|
||||||
match self.attributes.links {
|
match self.attributes.links {
|
||||||
Preserve::No => false,
|
Preserve::No { .. } => false,
|
||||||
Preserve::Yes { .. } => true,
|
Preserve::Yes { .. } => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn preserve_mode(&self) -> (bool, bool) {
|
||||||
|
match self.attributes.mode {
|
||||||
|
Preserve::No { explicit } => match explicit {
|
||||||
|
true => (false, true),
|
||||||
|
false => (false, false),
|
||||||
|
},
|
||||||
|
Preserve::Yes { .. } => (true, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether to force overwriting the destination file.
|
/// Whether to force overwriting the destination file.
|
||||||
fn force(&self) -> bool {
|
fn force(&self) -> bool {
|
||||||
matches!(self.overwrite, OverwriteMode::Clobber(ClobberMode::Force))
|
matches!(self.overwrite, OverwriteMode::Clobber(ClobberMode::Force))
|
||||||
|
@ -1000,7 +1081,7 @@ impl TargetType {
|
||||||
///
|
///
|
||||||
/// Treat target as a dir if we have multiple sources or the target
|
/// Treat target as a dir if we have multiple sources or the target
|
||||||
/// exists and already is a directory
|
/// exists and already is a directory
|
||||||
fn determine(sources: &[Source], target: &TargetSlice) -> Self {
|
fn determine(sources: &[PathBuf], target: &Path) -> Self {
|
||||||
if sources.len() > 1 || target.is_dir() {
|
if sources.len() > 1 || target.is_dir() {
|
||||||
Self::Directory
|
Self::Directory
|
||||||
} else {
|
} else {
|
||||||
|
@ -1010,10 +1091,16 @@ impl TargetType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns tuple of (Source paths, Target)
|
/// Returns tuple of (Source paths, Target)
|
||||||
fn parse_path_args(mut paths: Vec<Source>, options: &Options) -> CopyResult<(Vec<Source>, Target)> {
|
fn parse_path_args(
|
||||||
|
mut paths: Vec<PathBuf>,
|
||||||
|
options: &Options,
|
||||||
|
) -> CopyResult<(Vec<PathBuf>, PathBuf)> {
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
// No files specified
|
// No files specified
|
||||||
return Err("missing file operand".into());
|
return Err("missing file operand".into());
|
||||||
|
} else if paths.len() == 1 && options.target_dir.is_none() {
|
||||||
|
// Only one file specified
|
||||||
|
return Err(format!("missing destination file operand after {:?}", paths[0]).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an error if the user requested to copy more than one
|
// Return an error if the user requested to copy more than one
|
||||||
|
@ -1044,66 +1131,6 @@ fn parse_path_args(mut paths: Vec<Source>, options: &Options) -> CopyResult<(Vec
|
||||||
Ok((paths, target))
|
Ok((paths, target))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the inode information for a file.
|
|
||||||
fn get_inode(file_info: &FileInformation) -> u64 {
|
|
||||||
#[cfg(unix)]
|
|
||||||
let result = file_info.inode();
|
|
||||||
#[cfg(windows)]
|
|
||||||
let result = file_info.file_index();
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "redox")]
|
|
||||||
fn preserve_hardlinks(
|
|
||||||
hard_links: &mut Vec<(String, u64)>,
|
|
||||||
source: &std::path::Path,
|
|
||||||
dest: &std::path::Path,
|
|
||||||
found_hard_link: &mut bool,
|
|
||||||
) -> CopyResult<()> {
|
|
||||||
// Redox does not currently support hard links
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hard link a pair of files if needed _and_ record if this pair is a new hard link.
|
|
||||||
#[cfg(not(target_os = "redox"))]
|
|
||||||
fn preserve_hardlinks(
|
|
||||||
hard_links: &mut Vec<(String, u64)>,
|
|
||||||
source: &std::path::Path,
|
|
||||||
dest: &std::path::Path,
|
|
||||||
) -> CopyResult<bool> {
|
|
||||||
let info = FileInformation::from_path(source, false)
|
|
||||||
.context(format!("cannot stat {}", source.quote()))?;
|
|
||||||
let inode = get_inode(&info);
|
|
||||||
let nlinks = info.number_of_links();
|
|
||||||
let mut found_hard_link = false;
|
|
||||||
for (link, link_inode) in hard_links.iter() {
|
|
||||||
if *link_inode == inode {
|
|
||||||
// Consider the following files:
|
|
||||||
//
|
|
||||||
// * `src/f` - a regular file
|
|
||||||
// * `src/link` - a hard link to `src/f`
|
|
||||||
// * `dest/src/f` - a different regular file
|
|
||||||
//
|
|
||||||
// In this scenario, if we do `cp -a src/ dest/`, it is
|
|
||||||
// possible that the order of traversal causes `src/link`
|
|
||||||
// to get copied first (to `dest/src/link`). In that case,
|
|
||||||
// in order to make sure `dest/src/link` is a hard link to
|
|
||||||
// `dest/src/f` and `dest/src/f` has the contents of
|
|
||||||
// `src/f`, we delete the existing file to allow the hard
|
|
||||||
// linking.
|
|
||||||
if file_or_link_exists(dest) && file_or_link_exists(Path::new(link)) {
|
|
||||||
std::fs::remove_file(dest)?;
|
|
||||||
}
|
|
||||||
std::fs::hard_link(link, dest).unwrap();
|
|
||||||
found_hard_link = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found_hard_link && nlinks > 1 {
|
|
||||||
hard_links.push((dest.to_str().unwrap().to_string(), inode));
|
|
||||||
}
|
|
||||||
Ok(found_hard_link)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When handling errors, we don't always want to show them to the user. This function handles that.
|
/// When handling errors, we don't always want to show them to the user. This function handles that.
|
||||||
fn show_error_if_needed(error: &Error) {
|
fn show_error_if_needed(error: &Error) {
|
||||||
match error {
|
match error {
|
||||||
|
@ -1122,25 +1149,29 @@ fn show_error_if_needed(error: &Error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy all `sources` to `target`. Returns an
|
/// Copy all `sources` to `target`.
|
||||||
/// `Err(Error::NotAllFilesCopied)` if at least one non-fatal error was
|
|
||||||
/// encountered.
|
|
||||||
///
|
///
|
||||||
/// Behavior depends on path`options`, see [`Options`] for details.
|
/// Returns an `Err(Error::NotAllFilesCopied)` if at least one non-fatal error
|
||||||
|
/// was encountered.
|
||||||
///
|
///
|
||||||
/// [`Options`]: ./struct.Options.html
|
/// Behavior is determined by the `options` parameter, see [`Options`] for details.
|
||||||
fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResult<()> {
|
pub fn copy(sources: &[PathBuf], target: &Path, options: &Options) -> CopyResult<()> {
|
||||||
let target_type = TargetType::determine(sources, target);
|
let target_type = TargetType::determine(sources, target);
|
||||||
verify_target_type(target, &target_type)?;
|
verify_target_type(target, &target_type)?;
|
||||||
|
|
||||||
let preserve_hard_links = options.preserve_hard_links();
|
|
||||||
|
|
||||||
let mut hard_links: Vec<(String, u64)> = vec![];
|
|
||||||
|
|
||||||
let mut non_fatal_errors = false;
|
let mut non_fatal_errors = false;
|
||||||
let mut seen_sources = HashSet::with_capacity(sources.len());
|
let mut seen_sources = HashSet::with_capacity(sources.len());
|
||||||
let mut symlinked_files = HashSet::new();
|
let mut symlinked_files = HashSet::new();
|
||||||
|
|
||||||
|
// to remember the copied files for further usage.
|
||||||
|
// the FileInformation implemented the Hash trait by using
|
||||||
|
// 1. inode number
|
||||||
|
// 2. device number
|
||||||
|
// the combination of a file's inode number and device number is unique throughout all the file systems.
|
||||||
|
//
|
||||||
|
// key is the source file's information and the value is the destination filepath.
|
||||||
|
let mut copied_files: HashMap<FileInformation, PathBuf> = HashMap::with_capacity(sources.len());
|
||||||
|
|
||||||
let progress_bar = if options.progress_bar {
|
let progress_bar = if options.progress_bar {
|
||||||
let pb = ProgressBar::new(disk_usage(sources, options.recursive)?)
|
let pb = ProgressBar::new(disk_usage(sources, options.recursive)?)
|
||||||
.with_style(
|
.with_style(
|
||||||
|
@ -1156,33 +1187,29 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
for source in sources.iter() {
|
for source in sources {
|
||||||
if seen_sources.contains(source) {
|
if seen_sources.contains(source) {
|
||||||
// FIXME: compare sources by the actual file they point to, not their path. (e.g. dir/file == dir/../dir/file in most cases)
|
// FIXME: compare sources by the actual file they point to, not their path. (e.g. dir/file == dir/../dir/file in most cases)
|
||||||
show_warning!("source {} specified more than once", source.quote());
|
show_warning!("source {} specified more than once", source.quote());
|
||||||
} else {
|
} else if let Err(error) = copy_source(
|
||||||
let found_hard_link = if preserve_hard_links && !source.is_dir() {
|
&progress_bar,
|
||||||
let dest = construct_dest_path(source, target, &target_type, options)?;
|
source,
|
||||||
preserve_hardlinks(&mut hard_links, source, &dest)?
|
target,
|
||||||
} else {
|
&target_type,
|
||||||
false
|
options,
|
||||||
};
|
&mut symlinked_files,
|
||||||
if !found_hard_link {
|
&mut copied_files,
|
||||||
if let Err(error) = copy_source(
|
) {
|
||||||
&progress_bar,
|
show_error_if_needed(&error);
|
||||||
source,
|
non_fatal_errors = true;
|
||||||
target,
|
|
||||||
&target_type,
|
|
||||||
options,
|
|
||||||
&mut symlinked_files,
|
|
||||||
) {
|
|
||||||
show_error_if_needed(&error);
|
|
||||||
non_fatal_errors = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
seen_sources.insert(source);
|
|
||||||
}
|
}
|
||||||
|
seen_sources.insert(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(pb) = progress_bar {
|
||||||
|
pb.finish();
|
||||||
|
}
|
||||||
|
|
||||||
if non_fatal_errors {
|
if non_fatal_errors {
|
||||||
Err(Error::NotAllFilesCopied)
|
Err(Error::NotAllFilesCopied)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1192,7 +1219,7 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
|
||||||
|
|
||||||
fn construct_dest_path(
|
fn construct_dest_path(
|
||||||
source_path: &Path,
|
source_path: &Path,
|
||||||
target: &TargetSlice,
|
target: &Path,
|
||||||
target_type: &TargetType,
|
target_type: &TargetType,
|
||||||
options: &Options,
|
options: &Options,
|
||||||
) -> CopyResult<PathBuf> {
|
) -> CopyResult<PathBuf> {
|
||||||
|
@ -1223,16 +1250,25 @@ fn construct_dest_path(
|
||||||
|
|
||||||
fn copy_source(
|
fn copy_source(
|
||||||
progress_bar: &Option<ProgressBar>,
|
progress_bar: &Option<ProgressBar>,
|
||||||
source: &SourceSlice,
|
source: &Path,
|
||||||
target: &TargetSlice,
|
target: &Path,
|
||||||
target_type: &TargetType,
|
target_type: &TargetType,
|
||||||
options: &Options,
|
options: &Options,
|
||||||
symlinked_files: &mut HashSet<FileInformation>,
|
symlinked_files: &mut HashSet<FileInformation>,
|
||||||
|
copied_files: &mut HashMap<FileInformation, PathBuf>,
|
||||||
) -> CopyResult<()> {
|
) -> CopyResult<()> {
|
||||||
let source_path = Path::new(&source);
|
let source_path = Path::new(&source);
|
||||||
if source_path.is_dir() {
|
if source_path.is_dir() {
|
||||||
// Copy as directory
|
// Copy as directory
|
||||||
copy_directory(progress_bar, source, target, options, symlinked_files, true)
|
copy_directory(
|
||||||
|
progress_bar,
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
options,
|
||||||
|
symlinked_files,
|
||||||
|
copied_files,
|
||||||
|
true,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Copy as file
|
// Copy as file
|
||||||
let dest = construct_dest_path(source_path, target, target_type, options)?;
|
let dest = construct_dest_path(source_path, target, target_type, options)?;
|
||||||
|
@ -1242,6 +1278,7 @@ fn copy_source(
|
||||||
dest.as_path(),
|
dest.as_path(),
|
||||||
options,
|
options,
|
||||||
symlinked_files,
|
symlinked_files,
|
||||||
|
copied_files,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if options.parents {
|
if options.parents {
|
||||||
|
@ -1254,23 +1291,16 @@ fn copy_source(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OverwriteMode {
|
impl OverwriteMode {
|
||||||
fn verify(&self, path: &Path, verbose: bool) -> CopyResult<()> {
|
fn verify(&self, path: &Path) -> CopyResult<()> {
|
||||||
match *self {
|
match *self {
|
||||||
Self::NoClobber => {
|
Self::NoClobber => {
|
||||||
if verbose {
|
eprintln!("{}: not replacing {}", util_name(), path.quote());
|
||||||
println!("skipped {}", path.quote());
|
|
||||||
} else {
|
|
||||||
eprintln!("{}: not replacing {}", util_name(), path.quote());
|
|
||||||
}
|
|
||||||
Err(Error::NotAllFilesCopied)
|
Err(Error::NotAllFilesCopied)
|
||||||
}
|
}
|
||||||
Self::Interactive(_) => {
|
Self::Interactive(_) => {
|
||||||
if prompt_yes!("overwrite {}?", path.quote()) {
|
if prompt_yes!("overwrite {}?", path.quote()) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
if verbose {
|
|
||||||
println!("skipped {}", path.quote());
|
|
||||||
}
|
|
||||||
Err(Error::Skipped)
|
Err(Error::Skipped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1284,7 +1314,7 @@ impl OverwriteMode {
|
||||||
/// If it's required, then the error is thrown.
|
/// If it's required, then the error is thrown.
|
||||||
fn handle_preserve<F: Fn() -> CopyResult<()>>(p: &Preserve, f: F) -> CopyResult<()> {
|
fn handle_preserve<F: Fn() -> CopyResult<()>>(p: &Preserve, f: F) -> CopyResult<()> {
|
||||||
match p {
|
match p {
|
||||||
Preserve::No => {}
|
Preserve::No { .. } => {}
|
||||||
Preserve::Yes { required } => {
|
Preserve::Yes { required } => {
|
||||||
let result = f();
|
let result = f();
|
||||||
if *required {
|
if *required {
|
||||||
|
@ -1478,7 +1508,7 @@ fn handle_existing_dest(
|
||||||
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
|
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
options.overwrite.verify(dest, options.verbose)?;
|
options.overwrite.verify(dest)?;
|
||||||
|
|
||||||
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
|
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
|
||||||
if let Some(backup_path) = backup_path {
|
if let Some(backup_path) = backup_path {
|
||||||
|
@ -1503,6 +1533,24 @@ fn handle_existing_dest(
|
||||||
OverwriteMode::Clobber(ClobberMode::RemoveDestination) => {
|
OverwriteMode::Clobber(ClobberMode::RemoveDestination) => {
|
||||||
fs::remove_file(dest)?;
|
fs::remove_file(dest)?;
|
||||||
}
|
}
|
||||||
|
OverwriteMode::Clobber(ClobberMode::Standard) => {
|
||||||
|
// Consider the following files:
|
||||||
|
//
|
||||||
|
// * `src/f` - a regular file
|
||||||
|
// * `src/link` - a hard link to `src/f`
|
||||||
|
// * `dest/src/f` - a different regular file
|
||||||
|
//
|
||||||
|
// In this scenario, if we do `cp -a src/ dest/`, it is
|
||||||
|
// possible that the order of traversal causes `src/link`
|
||||||
|
// to get copied first (to `dest/src/link`). In that case,
|
||||||
|
// in order to make sure `dest/src/link` is a hard link to
|
||||||
|
// `dest/src/f` and `dest/src/f` has the contents of
|
||||||
|
// `src/f`, we delete the existing file to allow the hard
|
||||||
|
// linking.
|
||||||
|
if options.preserve_hard_links() {
|
||||||
|
fs::remove_file(dest)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1576,6 +1624,7 @@ fn copy_file(
|
||||||
dest: &Path,
|
dest: &Path,
|
||||||
options: &Options,
|
options: &Options,
|
||||||
symlinked_files: &mut HashSet<FileInformation>,
|
symlinked_files: &mut HashSet<FileInformation>,
|
||||||
|
copied_files: &mut HashMap<FileInformation, PathBuf>,
|
||||||
source_in_command_line: bool,
|
source_in_command_line: bool,
|
||||||
) -> CopyResult<()> {
|
) -> CopyResult<()> {
|
||||||
if (options.update == UpdateMode::ReplaceIfOlder || options.update == UpdateMode::ReplaceNone)
|
if (options.update == UpdateMode::ReplaceIfOlder || options.update == UpdateMode::ReplaceNone)
|
||||||
|
@ -1613,12 +1662,33 @@ fn copy_file(
|
||||||
dest.display()
|
dest.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
if paths_refer_to_same_file(source, dest, true)
|
||||||
|
&& matches!(
|
||||||
|
options.overwrite,
|
||||||
|
OverwriteMode::Clobber(ClobberMode::RemoveDestination)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
fs::remove_file(dest)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if file_or_link_exists(dest) {
|
if file_or_link_exists(dest) {
|
||||||
handle_existing_dest(source, dest, options, source_in_command_line)?;
|
handle_existing_dest(source, dest, options, source_in_command_line)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.preserve_hard_links() {
|
||||||
|
// if we encounter a matching device/inode pair in the source tree
|
||||||
|
// we can arrange to create a hard link between the corresponding names
|
||||||
|
// in the destination tree.
|
||||||
|
if let Some(new_source) = copied_files.get(
|
||||||
|
&FileInformation::from_path(source, options.dereference(source_in_command_line))
|
||||||
|
.context(format!("cannot stat {}", source.quote()))?,
|
||||||
|
) {
|
||||||
|
std::fs::hard_link(new_source, dest)?;
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if options.verbose {
|
if options.verbose {
|
||||||
if let Some(pb) = progress_bar {
|
if let Some(pb) = progress_bar {
|
||||||
// Suspend (hide) the progress bar so the println won't overlap with the progress bar.
|
// Suspend (hide) the progress bar so the println won't overlap with the progress bar.
|
||||||
|
@ -1681,15 +1751,10 @@ fn copy_file(
|
||||||
let mut permissions = source_metadata.permissions();
|
let mut permissions = source_metadata.permissions();
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
use uucore::mode::get_umask;
|
let mut mode = handle_no_preserve_mode(options, permissions.mode());
|
||||||
|
|
||||||
let mut mode = permissions.mode();
|
|
||||||
|
|
||||||
// remove sticky bit, suid and gid bit
|
|
||||||
const SPECIAL_PERMS_MASK: u32 = 0o7000;
|
|
||||||
mode &= !SPECIAL_PERMS_MASK;
|
|
||||||
|
|
||||||
// apply umask
|
// apply umask
|
||||||
|
use uucore::mode::get_umask;
|
||||||
mode &= !get_umask();
|
mode &= !get_umask();
|
||||||
|
|
||||||
permissions.set_mode(mode);
|
permissions.set_mode(mode);
|
||||||
|
@ -1806,6 +1871,11 @@ fn copy_file(
|
||||||
|
|
||||||
copy_attributes(source, dest, &options.attributes)?;
|
copy_attributes(source, dest, &options.attributes)?;
|
||||||
|
|
||||||
|
copied_files.insert(
|
||||||
|
FileInformation::from_path(source, options.dereference(source_in_command_line))?,
|
||||||
|
dest.to_path_buf(),
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(progress_bar) = progress_bar {
|
if let Some(progress_bar) = progress_bar {
|
||||||
progress_bar.inc(fs::metadata(source)?.len());
|
progress_bar.inc(fs::metadata(source)?.len());
|
||||||
}
|
}
|
||||||
|
@ -1813,6 +1883,49 @@ fn copy_file(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn handle_no_preserve_mode(options: &Options, org_mode: u32) -> u32 {
|
||||||
|
let (is_preserve_mode, is_explicit_no_preserve_mode) = options.preserve_mode();
|
||||||
|
if !is_preserve_mode {
|
||||||
|
use libc::{
|
||||||
|
S_IRGRP, S_IROTH, S_IRUSR, S_IRWXG, S_IRWXO, S_IRWXU, S_IWGRP, S_IWOTH, S_IWUSR,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "macos-12",
|
||||||
|
target_os = "freebsd",
|
||||||
|
)))]
|
||||||
|
{
|
||||||
|
const MODE_RW_UGO: u32 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||||
|
const S_IRWXUGO: u32 = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||||
|
match is_explicit_no_preserve_mode {
|
||||||
|
true => return MODE_RW_UGO,
|
||||||
|
false => return org_mode & S_IRWXUGO,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "macos-12",
|
||||||
|
target_os = "freebsd",
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
const MODE_RW_UGO: u32 =
|
||||||
|
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) as u32;
|
||||||
|
const S_IRWXUGO: u32 = (S_IRWXU | S_IRWXG | S_IRWXO) as u32;
|
||||||
|
match is_explicit_no_preserve_mode {
|
||||||
|
true => return MODE_RW_UGO,
|
||||||
|
false => return org_mode & S_IRWXUGO,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
org_mode
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy the file from `source` to `dest` either using the normal `fs::copy` or a
|
/// Copy the file from `source` to `dest` either using the normal `fs::copy` or a
|
||||||
/// copy-on-write scheme if --reflink is specified and the filesystem supports it.
|
/// copy-on-write scheme if --reflink is specified and the filesystem supports it.
|
||||||
fn copy_helper(
|
fn copy_helper(
|
||||||
|
@ -1836,7 +1949,7 @@ fn copy_helper(
|
||||||
File::create(dest).context(dest.display().to_string())?;
|
File::create(dest).context(dest.display().to_string())?;
|
||||||
} else if source_is_fifo && options.recursive && !options.copy_contents {
|
} else if source_is_fifo && options.recursive && !options.copy_contents {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
copy_fifo(dest, options.overwrite, options.verbose)?;
|
copy_fifo(dest, options.overwrite)?;
|
||||||
} else if source_is_symlink {
|
} else if source_is_symlink {
|
||||||
copy_link(source, dest, symlinked_files)?;
|
copy_link(source, dest, symlinked_files)?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1861,9 +1974,9 @@ fn copy_helper(
|
||||||
// "Copies" a FIFO by creating a new one. This workaround is because Rust's
|
// "Copies" a FIFO by creating a new one. This workaround is because Rust's
|
||||||
// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
|
// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn copy_fifo(dest: &Path, overwrite: OverwriteMode, verbose: bool) -> CopyResult<()> {
|
fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> {
|
||||||
if dest.exists() {
|
if dest.exists() {
|
||||||
overwrite.verify(dest, verbose)?;
|
overwrite.verify(dest)?;
|
||||||
fs::remove_file(dest)?;
|
fs::remove_file(dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 ficlone reflink ftruncate pwrite fiemap
|
// spell-checker:ignore ficlone reflink ftruncate pwrite fiemap
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 reflink
|
// spell-checker:ignore reflink
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
|
@ -63,8 +63,15 @@ pub(crate) fn copy_on_write(
|
||||||
{
|
{
|
||||||
// clonefile(2) fails if the destination exists. Remove it and try again. Do not
|
// clonefile(2) fails if the destination exists. Remove it and try again. Do not
|
||||||
// bother to check if removal worked because we're going to try to clone again.
|
// bother to check if removal worked because we're going to try to clone again.
|
||||||
let _ = fs::remove_file(dest);
|
// first lets make sure the dest file is not read only
|
||||||
error = pfn(src.as_ptr(), dst.as_ptr(), 0);
|
if fs::metadata(dest).map_or(false, |md| !md.permissions().readonly()) {
|
||||||
|
// remove and copy again
|
||||||
|
// TODO: rewrite this to better match linux behavior
|
||||||
|
// linux first opens the source file and destination file then uses the file
|
||||||
|
// descriptors to do the clone.
|
||||||
|
let _ = fs::remove_file(dest);
|
||||||
|
error = pfn(src.as_ptr(), dst.as_ptr(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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.
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod macos;
|
mod macos;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 reflink
|
// spell-checker:ignore reflink
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_csplit"
|
name = "uu_csplit"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
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"
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
#![crate_name = "uu_csplit"]
|
#![crate_name = "uu_csplit"]
|
||||||
// spell-checker:ignore rustdoc
|
// spell-checker:ignore rustdoc
|
||||||
#![allow(rustdoc::private_intra_doc_links)]
|
#![allow(rustdoc::private_intra_doc_links)]
|
||||||
|
@ -660,6 +664,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn input_splitter() {
|
fn input_splitter() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
Ok(String::from("aaa")),
|
Ok(String::from("aaa")),
|
||||||
|
@ -732,6 +737,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn input_splitter_interrupt_rewind() {
|
fn input_splitter_interrupt_rewind() {
|
||||||
let input = vec![
|
let input = vec![
|
||||||
Ok(String::from("aaa")),
|
Ok(String::from("aaa")),
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
use std::io;
|
use std::io;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore (regex) SKIPTO UPTO ; (vars) ntimes
|
// spell-checker:ignore (regex) SKIPTO UPTO ; (vars) ntimes
|
||||||
|
|
||||||
use crate::csplit_error::CsplitError;
|
use crate::csplit_error::CsplitError;
|
||||||
|
@ -207,6 +211,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn up_to_match_pattern() {
|
fn up_to_match_pattern() {
|
||||||
let input: Vec<String> = vec![
|
let input: Vec<String> = vec![
|
||||||
"/test1.*end$/",
|
"/test1.*end$/",
|
||||||
|
@ -260,6 +265,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn skip_to_match_pattern() {
|
fn skip_to_match_pattern() {
|
||||||
let input: Vec<String> = vec![
|
let input: Vec<String> = vec![
|
||||||
"%test1.*end$%",
|
"%test1.*end$%",
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore (regex) diuox
|
// spell-checker:ignore (regex) diuox
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_cut"
|
name = "uu_cut"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
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,10 +16,9 @@ path = "src/cut.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true }
|
uucore = { workspace = true, features = ["ranges"] }
|
||||||
memchr = { workspace = true }
|
memchr = { workspace = true }
|
||||||
bstr = { workspace = true }
|
bstr = { workspace = true }
|
||||||
is-terminal = { workspace = true }
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "cut"
|
name = "cut"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Rolf Morel <rolfmorel@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.
|
||||||
|
|
||||||
|
@ -9,12 +7,12 @@
|
||||||
|
|
||||||
use bstr::io::BufReadExt;
|
use bstr::io::BufReadExt;
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use is_terminal::IsTerminal;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
|
use std::io::{stdin, stdout, BufReader, BufWriter, IsTerminal, Read, Write};
|
||||||
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};
|
||||||
|
use uucore::line_ending::LineEnding;
|
||||||
|
|
||||||
use self::searcher::Searcher;
|
use self::searcher::Searcher;
|
||||||
use matcher::{ExactMatcher, Matcher, WhitespaceMatcher};
|
use matcher::{ExactMatcher, Matcher, WhitespaceMatcher};
|
||||||
|
@ -30,7 +28,7 @@ const AFTER_HELP: &str = help_section!("after help", "cut.md");
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
out_delim: Option<String>,
|
out_delim: Option<String>,
|
||||||
zero_terminated: bool,
|
line_ending: LineEnding,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Delimiter {
|
enum Delimiter {
|
||||||
|
@ -42,7 +40,7 @@ struct FieldOptions {
|
||||||
delimiter: Delimiter,
|
delimiter: Delimiter,
|
||||||
out_delimiter: Option<String>,
|
out_delimiter: Option<String>,
|
||||||
only_delimited: bool,
|
only_delimited: bool,
|
||||||
zero_terminated: bool,
|
line_ending: LineEnding,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Mode {
|
enum Mode {
|
||||||
|
@ -68,7 +66,7 @@ fn list_to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> UResult<()> {
|
fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> UResult<()> {
|
||||||
let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
|
let newline_char = opts.line_ending.into();
|
||||||
let mut buf_in = BufReader::new(reader);
|
let mut buf_in = BufReader::new(reader);
|
||||||
let mut out = stdout_writer();
|
let mut out = stdout_writer();
|
||||||
let delim = opts
|
let delim = opts
|
||||||
|
@ -259,7 +257,7 @@ fn cut_fields_implicit_out_delim<R: Read, M: Matcher>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> UResult<()> {
|
fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> UResult<()> {
|
||||||
let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
|
let newline_char = opts.line_ending.into();
|
||||||
match opts.delimiter {
|
match opts.delimiter {
|
||||||
Delimiter::String(ref delim) => {
|
Delimiter::String(ref delim) => {
|
||||||
let matcher = ExactMatcher::new(delim.as_bytes());
|
let matcher = ExactMatcher::new(delim.as_bytes());
|
||||||
|
@ -376,7 +374,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
zero_terminated: matches.get_flag(options::ZERO_TERMINATED),
|
line_ending: LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED)),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -391,7 +389,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
),
|
),
|
||||||
zero_terminated: matches.get_flag(options::ZERO_TERMINATED),
|
line_ending: LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED)),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -411,6 +409,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let only_delimited = matches.get_flag(options::ONLY_DELIMITED);
|
let only_delimited = matches.get_flag(options::ONLY_DELIMITED);
|
||||||
let whitespace_delimited = matches.get_flag(options::WHITESPACE_DELIMITED);
|
let whitespace_delimited = matches.get_flag(options::WHITESPACE_DELIMITED);
|
||||||
let zero_terminated = matches.get_flag(options::ZERO_TERMINATED);
|
let zero_terminated = matches.get_flag(options::ZERO_TERMINATED);
|
||||||
|
let line_ending = LineEnding::from_zero_flag(zero_terminated);
|
||||||
|
|
||||||
match matches.get_one::<String>(options::DELIMITER).map(|s| s.as_str()) {
|
match matches.get_one::<String>(options::DELIMITER).map(|s| s.as_str()) {
|
||||||
Some(_) if whitespace_delimited => {
|
Some(_) if whitespace_delimited => {
|
||||||
|
@ -441,7 +440,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
delimiter: Delimiter::String(delim),
|
delimiter: Delimiter::String(delim),
|
||||||
out_delimiter: out_delim,
|
out_delimiter: out_delim,
|
||||||
only_delimited,
|
only_delimited,
|
||||||
zero_terminated,
|
line_ending,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -455,7 +454,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
},
|
},
|
||||||
out_delimiter: out_delim,
|
out_delimiter: out_delim,
|
||||||
only_delimited,
|
only_delimited,
|
||||||
zero_terminated,
|
line_ending,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Rolf Morel <rolfmorel@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.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# spell-checker:ignore datetime
|
# spell-checker:ignore datetime
|
||||||
[package]
|
[package]
|
||||||
name = "uu_date"
|
name = "uu_date"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
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"
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Anthony Deschamps <anthony.j.deschamps@gmail.com>
|
|
||||||
// (c) Sylvestre Ledru <sylvestre@debian.org>
|
|
||||||
//
|
|
||||||
// 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.
|
||||||
|
|
||||||
|
@ -169,7 +166,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
||||||
if let Ok(duration) = parse_datetime::from_str(date.as_str()) {
|
let ref_time = Local::now();
|
||||||
|
if let Ok(new_time) = parse_datetime::parse_datetime_at_date(ref_time, date.as_str()) {
|
||||||
|
let duration = new_time.signed_duration_since(ref_time);
|
||||||
DateSource::Human(duration)
|
DateSource::Human(duration)
|
||||||
} else {
|
} else {
|
||||||
DateSource::Custom(date.into())
|
DateSource::Custom(date.into())
|
||||||
|
@ -227,8 +226,20 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
DateSource::Human(relative_time) => {
|
DateSource::Human(relative_time) => {
|
||||||
// Get the current DateTime<FixedOffset> for things like "1 year ago"
|
// Get the current DateTime<FixedOffset> for things like "1 year ago"
|
||||||
let current_time = DateTime::<FixedOffset>::from(Local::now());
|
let current_time = DateTime::<FixedOffset>::from(Local::now());
|
||||||
let iter = std::iter::once(Ok(current_time + relative_time));
|
// double check the result is overflow or not of the current_time + relative_time
|
||||||
Box::new(iter)
|
// it may cause a panic of chrono::datetime::DateTime add
|
||||||
|
match current_time.checked_add_signed(relative_time) {
|
||||||
|
Some(date) => {
|
||||||
|
let iter = std::iter::once(Ok(date));
|
||||||
|
Box::new(iter)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(USimpleError::new(
|
||||||
|
1,
|
||||||
|
format!("invalid date {}", relative_time),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DateSource::File(ref path) => {
|
DateSource::File(ref path) => {
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_dd"
|
name = "uu_dd"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "dd ~ (uutils) copy and convert files"
|
description = "dd ~ (uutils) copy and convert files"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 datastructures rstat rposition cflags ctable
|
// spell-checker:ignore datastructures rstat rposition cflags ctable
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Tyler Steele <tyler.steele@protonmail.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.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Tyler Steele <tyler.steele@protonmail.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.
|
||||||
// spell-checker:ignore ctable, outfile, iseek, oseek
|
// spell-checker:ignore ctable, outfile, iseek, oseek
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Tyler Steele <tyler.steele@protonmail.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.
|
||||||
|
|
||||||
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE
|
// spell-checker:ignore fname, ftype, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE
|
||||||
|
|
||||||
mod datastructures;
|
mod datastructures;
|
||||||
use datastructures::*;
|
use datastructures::*;
|
||||||
|
@ -51,6 +49,8 @@ use nix::{
|
||||||
fcntl::{posix_fadvise, PosixFadviseAdvice},
|
fcntl::{posix_fadvise, PosixFadviseAdvice},
|
||||||
};
|
};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use uucore::error::set_exit_code;
|
||||||
use uucore::error::{FromIo, UResult};
|
use uucore::error::{FromIo, UResult};
|
||||||
use uucore::{format_usage, help_about, help_section, help_usage, show_error};
|
use uucore::{format_usage, help_about, help_section, help_usage, show_error};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
@ -202,14 +202,25 @@ impl Source {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
},
|
},
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
Self::StdinFile(f) => match io::copy(&mut f.take(n), &mut io::sink()) {
|
Self::StdinFile(f) => {
|
||||||
Ok(m) if m < n => {
|
if let Ok(Some(len)) = try_get_len_of_block_device(f) {
|
||||||
show_error!("'standard input': cannot skip to specified offset");
|
if len < n {
|
||||||
Ok(m)
|
// GNU compatibility:
|
||||||
|
// this case prints the stats but sets the exit code to 1
|
||||||
|
show_error!("'standard input': cannot skip: Invalid argument");
|
||||||
|
set_exit_code(1);
|
||||||
|
return Ok(len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(m) => Ok(m),
|
match io::copy(&mut f.take(n), &mut io::sink()) {
|
||||||
Err(e) => Err(e),
|
Ok(m) if m < n => {
|
||||||
},
|
show_error!("'standard input': cannot skip to specified offset");
|
||||||
|
Ok(m)
|
||||||
|
}
|
||||||
|
Ok(m) => Ok(m),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::File(f) => f.seek(io::SeekFrom::Start(n)),
|
Self::File(f) => f.seek(io::SeekFrom::Start(n)),
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
|
Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
|
||||||
|
@ -529,7 +540,19 @@ impl Dest {
|
||||||
fn seek(&mut self, n: u64) -> io::Result<u64> {
|
fn seek(&mut self, n: u64) -> io::Result<u64> {
|
||||||
match self {
|
match self {
|
||||||
Self::Stdout(stdout) => io::copy(&mut io::repeat(0).take(n), stdout),
|
Self::Stdout(stdout) => io::copy(&mut io::repeat(0).take(n), stdout),
|
||||||
Self::File(f, _) => f.seek(io::SeekFrom::Start(n)),
|
Self::File(f, _) => {
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Ok(Some(len)) = try_get_len_of_block_device(f) {
|
||||||
|
if len < n {
|
||||||
|
// GNU compatibility:
|
||||||
|
// this case prints the stats but sets the exit code to 1
|
||||||
|
show_error!("'standard output': cannot seek: Invalid argument");
|
||||||
|
set_exit_code(1);
|
||||||
|
return Ok(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.seek(io::SeekFrom::Start(n))
|
||||||
|
}
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
Self::Fifo(f) => {
|
Self::Fifo(f) => {
|
||||||
// Seeking in a named pipe means *reading* from the pipe.
|
// Seeking in a named pipe means *reading* from the pipe.
|
||||||
|
@ -1135,6 +1158,20 @@ fn is_stdout_redirected_to_seekable_file() -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to get the len if it is a block device
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn try_get_len_of_block_device(file: &mut File) -> io::Result<Option<u64>> {
|
||||||
|
let ftype = file.metadata()?.file_type();
|
||||||
|
if !ftype.is_block_device() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this can be replaced by file.stream_len() when stable.
|
||||||
|
let len = file.seek(SeekFrom::End(0))?;
|
||||||
|
file.rewind()?;
|
||||||
|
Ok(Some(len))
|
||||||
|
}
|
||||||
|
|
||||||
/// Decide whether the named file is a named pipe, also known as a FIFO.
|
/// Decide whether the named file is a named pipe, also known as a FIFO.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn is_fifo(filename: &str) -> bool {
|
fn is_fifo(filename: &str) -> bool {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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.
|
||||||
/// Functions for formatting a number as a magnitude and a unit suffix.
|
/// Functions for formatting a number as a magnitude and a unit suffix.
|
||||||
|
|
||||||
/// The first ten powers of 1024.
|
/// The first ten powers of 1024.
|
||||||
|
@ -115,6 +115,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn test_to_magnitude_and_suffix_not_powers_of_1024() {
|
fn test_to_magnitude_and_suffix_not_powers_of_1024() {
|
||||||
assert_eq!(to_magnitude_and_suffix(1, SuffixType::Si), "1.0 B");
|
assert_eq!(to_magnitude_and_suffix(1, SuffixType::Si), "1.0 B");
|
||||||
assert_eq!(to_magnitude_and_suffix(999, SuffixType::Si), "999 B");
|
assert_eq!(to_magnitude_and_suffix(999, SuffixType::Si), "999 B");
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Tyler Steele <tyler.steele@protonmail.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.
|
||||||
// spell-checker:ignore ctty, ctable, iseek, oseek, iconvflags, oconvflags parseargs outfile oconv
|
// spell-checker:ignore ctty, ctable, iseek, oseek, iconvflags, oconvflags parseargs outfile oconv
|
||||||
|
@ -247,29 +245,29 @@ impl Parser {
|
||||||
None => return Err(ParseError::UnrecognizedOperand(operand.to_string())),
|
None => return Err(ParseError::UnrecognizedOperand(operand.to_string())),
|
||||||
Some((k, v)) => match k {
|
Some((k, v)) => match k {
|
||||||
"bs" => {
|
"bs" => {
|
||||||
let bs = self.parse_bytes(k, v)?;
|
let bs = Self::parse_bytes(k, v)?;
|
||||||
self.ibs = bs;
|
self.ibs = bs;
|
||||||
self.obs = bs;
|
self.obs = bs;
|
||||||
}
|
}
|
||||||
"cbs" => self.cbs = Some(self.parse_bytes(k, v)?),
|
"cbs" => self.cbs = Some(Self::parse_bytes(k, v)?),
|
||||||
"conv" => self.parse_conv_flags(v)?,
|
"conv" => self.parse_conv_flags(v)?,
|
||||||
"count" => self.count = Some(self.parse_n(v)?),
|
"count" => self.count = Some(Self::parse_n(v)?),
|
||||||
"ibs" => self.ibs = self.parse_bytes(k, v)?,
|
"ibs" => self.ibs = Self::parse_bytes(k, v)?,
|
||||||
"if" => self.infile = Some(v.to_string()),
|
"if" => self.infile = Some(v.to_string()),
|
||||||
"iflag" => self.parse_input_flags(v)?,
|
"iflag" => self.parse_input_flags(v)?,
|
||||||
"obs" => self.obs = self.parse_bytes(k, v)?,
|
"obs" => self.obs = Self::parse_bytes(k, v)?,
|
||||||
"of" => self.outfile = Some(v.to_string()),
|
"of" => self.outfile = Some(v.to_string()),
|
||||||
"oflag" => self.parse_output_flags(v)?,
|
"oflag" => self.parse_output_flags(v)?,
|
||||||
"seek" | "oseek" => self.seek = self.parse_n(v)?,
|
"seek" | "oseek" => self.seek = Self::parse_n(v)?,
|
||||||
"skip" | "iseek" => self.skip = self.parse_n(v)?,
|
"skip" | "iseek" => self.skip = Self::parse_n(v)?,
|
||||||
"status" => self.status = Some(self.parse_status_level(v)?),
|
"status" => self.status = Some(Self::parse_status_level(v)?),
|
||||||
_ => return Err(ParseError::UnrecognizedOperand(operand.to_string())),
|
_ => return Err(ParseError::UnrecognizedOperand(operand.to_string())),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_n(&self, val: &str) -> Result<Num, ParseError> {
|
fn parse_n(val: &str) -> Result<Num, ParseError> {
|
||||||
let n = parse_bytes_with_opt_multiplier(val)?;
|
let n = parse_bytes_with_opt_multiplier(val)?;
|
||||||
Ok(if val.ends_with('B') {
|
Ok(if val.ends_with('B') {
|
||||||
Num::Bytes(n)
|
Num::Bytes(n)
|
||||||
|
@ -278,13 +276,13 @@ impl Parser {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_bytes(&self, arg: &str, val: &str) -> Result<usize, ParseError> {
|
fn parse_bytes(arg: &str, val: &str) -> Result<usize, ParseError> {
|
||||||
parse_bytes_with_opt_multiplier(val)?
|
parse_bytes_with_opt_multiplier(val)?
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| ParseError::BsOutOfRange(arg.to_string()))
|
.map_err(|_| ParseError::BsOutOfRange(arg.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_status_level(&self, val: &str) -> Result<StatusLevel, ParseError> {
|
fn parse_status_level(val: &str) -> Result<StatusLevel, ParseError> {
|
||||||
match val {
|
match val {
|
||||||
"none" => Ok(StatusLevel::None),
|
"none" => Ok(StatusLevel::None),
|
||||||
"noxfer" => Ok(StatusLevel::Noxfer),
|
"noxfer" => Ok(StatusLevel::Noxfer),
|
||||||
|
@ -506,7 +504,7 @@ fn parse_bytes_no_x(full: &str, s: &str) -> Result<u64, ParseError> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let (num, multiplier) = match (s.find('c'), s.rfind('w'), s.rfind('b')) {
|
let (num, multiplier) = match (s.find('c'), s.rfind('w'), s.rfind('b')) {
|
||||||
(None, None, None) => match parser.parse(s) {
|
(None, None, None) => match parser.parse_u64(s) {
|
||||||
Ok(n) => (n, 1),
|
Ok(n) => (n, 1),
|
||||||
Err(ParseSizeError::InvalidSuffix(_) | ParseSizeError::ParseFailure(_)) => {
|
Err(ParseSizeError::InvalidSuffix(_) | ParseSizeError::ParseFailure(_)) => {
|
||||||
return Err(ParseError::InvalidNumber(full.to_string()))
|
return Err(ParseError::InvalidNumber(full.to_string()))
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat, oconv
|
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat, oconv
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -99,6 +103,7 @@ fn test_status_level_none() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn test_all_top_level_args_no_leading_dashes() {
|
fn test_all_top_level_args_no_leading_dashes() {
|
||||||
let args = &[
|
let args = &[
|
||||||
"if=foo.file",
|
"if=foo.file",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 btotal sigval
|
// spell-checker:ignore btotal sigval
|
||||||
//! Read and write progress tracking for dd.
|
//! Read and write progress tracking for dd.
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_df"
|
name = "uu_df"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "df ~ (uutils) display file system information"
|
description = "df ~ (uutils) display file system information"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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.
|
||||||
//! Types for representing and displaying block sizes.
|
//! Types for representing and displaying block sizes.
|
||||||
use crate::{OPT_BLOCKSIZE, OPT_PORTABILITY};
|
use crate::{OPT_BLOCKSIZE, OPT_PORTABILITY};
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
@ -9,7 +9,7 @@ use std::{env, fmt};
|
||||||
|
|
||||||
use uucore::{
|
use uucore::{
|
||||||
display::Quotable,
|
display::Quotable,
|
||||||
parse_size::{parse_size, ParseSizeError},
|
parse_size::{parse_size_u64, ParseSizeError},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The first ten powers of 1024.
|
/// The first ten powers of 1024.
|
||||||
|
@ -165,7 +165,7 @@ impl Default for BlockSize {
|
||||||
pub(crate) fn read_block_size(matches: &ArgMatches) -> Result<BlockSize, ParseSizeError> {
|
pub(crate) fn read_block_size(matches: &ArgMatches) -> Result<BlockSize, ParseSizeError> {
|
||||||
if matches.contains_id(OPT_BLOCKSIZE) {
|
if matches.contains_id(OPT_BLOCKSIZE) {
|
||||||
let s = matches.get_one::<String>(OPT_BLOCKSIZE).unwrap();
|
let s = matches.get_one::<String>(OPT_BLOCKSIZE).unwrap();
|
||||||
let bytes = parse_size(s)?;
|
let bytes = parse_size_u64(s)?;
|
||||||
|
|
||||||
if bytes > 0 {
|
if bytes > 0 {
|
||||||
Ok(BlockSize::Bytes(bytes))
|
Ok(BlockSize::Bytes(bytes))
|
||||||
|
@ -184,7 +184,7 @@ pub(crate) fn read_block_size(matches: &ArgMatches) -> Result<BlockSize, ParseSi
|
||||||
fn block_size_from_env() -> Option<u64> {
|
fn block_size_from_env() -> Option<u64> {
|
||||||
for env_var in ["DF_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"] {
|
for env_var in ["DF_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"] {
|
||||||
if let Ok(env_size) = env::var(env_var) {
|
if let Ok(env_size) = env::var(env_var) {
|
||||||
if let Ok(size) = parse_size(&env_size) {
|
if let Ok(size) = parse_size_u64(&env_size) {
|
||||||
return Some(size);
|
return Some(size);
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -239,6 +239,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn test_to_magnitude_and_suffix_not_powers_of_1024() {
|
fn test_to_magnitude_and_suffix_not_powers_of_1024() {
|
||||||
assert_eq!(to_magnitude_and_suffix(1, SuffixType::Si), "1B");
|
assert_eq!(to_magnitude_and_suffix(1, SuffixType::Si), "1B");
|
||||||
assert_eq!(to_magnitude_and_suffix(999, SuffixType::Si), "999B");
|
assert_eq!(to_magnitude_and_suffix(999, SuffixType::Si), "999B");
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 itotal iused iavail ipcent pcent squashfs
|
// spell-checker:ignore itotal iused iavail ipcent pcent squashfs
|
||||||
use crate::{OPT_INODES, OPT_OUTPUT, OPT_PRINT_TYPE};
|
use crate::{OPT_INODES, OPT_OUTPUT, OPT_PRINT_TYPE};
|
||||||
use clap::{parser::ValueSource, ArgMatches};
|
use clap::{parser::ValueSource, ArgMatches};
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
// This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
//
|
//
|
||||||
// (c) Fangxu Hu <framlog@gmail.com>
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// (c) Sylvestre Ledru <sylvestre@debian.org>
|
// file that was distributed with this source code.
|
||||||
//
|
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
|
||||||
// that was distributed with this source code.
|
|
||||||
// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs lofs
|
// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs lofs
|
||||||
mod blocks;
|
mod blocks;
|
||||||
mod columns;
|
mod columns;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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.
|
||||||
//! Provides a summary representation of a filesystem.
|
//! Provides a summary representation of a filesystem.
|
||||||
//!
|
//!
|
||||||
//! A [`Filesystem`] struct represents a device containing a
|
//! A [`Filesystem`] struct represents a device containing a
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * 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 tmpfs Pcent Itotal Iused Iavail Ipcent
|
// spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent nosuid nodev
|
||||||
//! The filesystem usage data table.
|
//! The filesystem usage data table.
|
||||||
//!
|
//!
|
||||||
//! A table ([`Table`]) comprises a header row ([`Header`]) and a
|
//! A table ([`Table`]) comprises a header row ([`Header`]) and a
|
||||||
|
@ -152,8 +152,10 @@ impl From<Filesystem> for Row {
|
||||||
ffree,
|
ffree,
|
||||||
..
|
..
|
||||||
} = fs.usage;
|
} = fs.usage;
|
||||||
let bused = blocks - bfree;
|
|
||||||
let fused = files - ffree;
|
// On Windows WSL, files can be less than ffree. Protect such cases via saturating_sub.
|
||||||
|
let bused = blocks.saturating_sub(bfree);
|
||||||
|
let fused = files.saturating_sub(ffree);
|
||||||
Self {
|
Self {
|
||||||
file: fs.file,
|
file: fs.file,
|
||||||
fs_device: dev_name,
|
fs_device: dev_name,
|
||||||
|
@ -815,4 +817,35 @@ mod tests {
|
||||||
assert_eq!(get_formatted_values(1000, 1000, 0), vec!("1", "1", "0"));
|
assert_eq!(get_formatted_values(1000, 1000, 0), vec!("1", "1", "0"));
|
||||||
assert_eq!(get_formatted_values(1001, 1000, 1), vec!("2", "1", "1"));
|
assert_eq!(get_formatted_values(1001, 1000, 1), vec!("2", "1", "1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_row_converter_with_invalid_numbers() {
|
||||||
|
// copy from wsl linux
|
||||||
|
let d = crate::Filesystem {
|
||||||
|
file: None,
|
||||||
|
mount_info: crate::MountInfo {
|
||||||
|
dev_id: "28".to_string(),
|
||||||
|
dev_name: "none".to_string(),
|
||||||
|
fs_type: "9p".to_string(),
|
||||||
|
mount_dir: "/usr/lib/wsl/drivers".to_string(),
|
||||||
|
mount_option: "ro,nosuid,nodev,noatime".to_string(),
|
||||||
|
mount_root: "/".to_string(),
|
||||||
|
remote: false,
|
||||||
|
dummy: false,
|
||||||
|
},
|
||||||
|
usage: crate::table::FsUsage {
|
||||||
|
blocksize: 4096,
|
||||||
|
blocks: 244029695,
|
||||||
|
bfree: 125085030,
|
||||||
|
bavail: 125085030,
|
||||||
|
bavail_top_bit_set: false,
|
||||||
|
files: 999,
|
||||||
|
ffree: 1000000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let row = Row::from(d);
|
||||||
|
|
||||||
|
assert_eq!(row.inodes_used, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_dir"
|
name = "uu_dir"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
authors = ["uutils developers"]
|
authors = ["uutils developers"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "shortcut to ls -C -b"
|
description = "shortcut to ls -C -b"
|
||||||
|
@ -16,7 +16,7 @@ path = "src/dir.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { workspace = true, features = ["env"] }
|
clap = { workspace = true, features = ["env"] }
|
||||||
uucore = { workspace = true, features = ["entries", "fs"] }
|
uucore = { workspace = true, features = ["entries", "fs", "quoting-style"] }
|
||||||
uu_ls = { workspace = true }
|
uu_ls = { workspace = true }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// * This file is part of the uutils coreutils package.
|
// This file is part of the uutils coreutils package.
|
||||||
// *
|
//
|
||||||
// * (c) gmnsii <gmnsii@protonmail.com>
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// *
|
// file that was distributed with this source code.
|
||||||
// * For the full copyright and license information, please view the LICENSE file
|
|
||||||
// * that was distributed with this source code.
|
|
||||||
|
|
||||||
use clap::Command;
|
use clap::Command;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "uu_dircolors"
|
name = "uu_dircolors"
|
||||||
version = "0.0.20"
|
version = "0.0.22"
|
||||||
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"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue