mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 03:57:44 +00:00
Merge branch 'master' of https://github.com/uutils/coreutils into ln/dst-symlink
This commit is contained in:
commit
5997853cc4
103 changed files with 949 additions and 592 deletions
104
.github/workflows/CICD.yml
vendored
104
.github/workflows/CICD.yml
vendored
|
@ -5,7 +5,7 @@ name: CICD
|
|||
# spell-checker:ignore (jargon) SHAs deps softprops toolchain
|
||||
# spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy
|
||||
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rustc rustfmt rustup shopt xargs
|
||||
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend tempfile testsuite uutils
|
||||
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend runtest tempfile testsuite uutils
|
||||
|
||||
env:
|
||||
PROJECT_NAME: coreutils
|
||||
|
@ -32,12 +32,12 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## VARs setup
|
||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||
# target-specific options
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='' ;
|
||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
outputs CARGO_FEATURES_OPTION
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
@ -93,12 +93,12 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## VARs setup
|
||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||
# target-specific options
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='' ;
|
||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
outputs CARGO_FEATURES_OPTION
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
@ -197,7 +197,11 @@ jobs:
|
|||
run: |
|
||||
bindir=$(pwd)/target/debug
|
||||
cd tmp/busybox-*/testsuite
|
||||
S=$(bindir=$bindir ./runtest) && printf "%s\n" "$S" || { printf "%s\n" "$S" | grep "FAIL:" | sed -e "s/FAIL: /::warning ::Test failure:/g" ; }
|
||||
## S=$(bindir=$bindir ./runtest) && printf "%s\n" "$S" || { printf "%s\n" "$S" | grep "FAIL:" | sed -e "s/FAIL: /::warning ::Test failure:/g" ; }
|
||||
output=$(bindir=$bindir ./runtest 2>&1 || true)
|
||||
printf "%s\n" "${output}"
|
||||
n_fails=$(echo "$output" | grep "^FAIL:\s" | wc --lines)
|
||||
if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi
|
||||
|
||||
makefile_build:
|
||||
name: Test the build target of the Makefile
|
||||
|
@ -261,22 +265,20 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## VARs setup
|
||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||
# toolchain
|
||||
TOOLCHAIN="stable" ## default to "stable" toolchain
|
||||
# * specify alternate/non-default TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: GH:rust-lang/rust#47048, GH:rust-lang/rust#53454, GH:rust-lang/cargo#6754)
|
||||
case ${{ matrix.job.target }} in *-pc-windows-gnu) TOOLCHAIN="stable-${{ matrix.job.target }}" ;; esac;
|
||||
# * use requested TOOLCHAIN if specified
|
||||
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
|
||||
echo set-output name=TOOLCHAIN::${TOOLCHAIN:-<empty>/false}
|
||||
echo ::set-output name=TOOLCHAIN::${TOOLCHAIN}
|
||||
outputs TOOLCHAIN
|
||||
# staging directory
|
||||
STAGING='_staging'
|
||||
echo set-output name=STAGING::${STAGING}
|
||||
echo ::set-output name=STAGING::${STAGING}
|
||||
outputs STAGING
|
||||
# determine EXE suffix
|
||||
EXE_suffix="" ; case '${{ matrix.job.target }}' in *-pc-windows-*) EXE_suffix=".exe" ;; esac;
|
||||
echo set-output name=EXE_suffix::${EXE_suffix}
|
||||
echo ::set-output name=EXE_suffix::${EXE_suffix}
|
||||
outputs EXE_suffix
|
||||
# parse commit reference info
|
||||
echo GITHUB_REF=${GITHUB_REF}
|
||||
echo GITHUB_SHA=${GITHUB_SHA}
|
||||
|
@ -284,14 +286,7 @@ jobs:
|
|||
unset REF_BRANCH ; case "${GITHUB_REF}" in refs/heads/*) REF_BRANCH=${GITHUB_REF#refs/heads/} ;; esac;
|
||||
unset REF_TAG ; case "${GITHUB_REF}" in refs/tags/*) REF_TAG=${GITHUB_REF#refs/tags/} ;; esac;
|
||||
REF_SHAS=${GITHUB_SHA:0:8}
|
||||
echo set-output name=REF_NAME::${REF_NAME}
|
||||
echo set-output name=REF_BRANCH::${REF_BRANCH}
|
||||
echo set-output name=REF_TAG::${REF_TAG}
|
||||
echo set-output name=REF_SHAS::${REF_SHAS}
|
||||
echo ::set-output name=REF_NAME::${REF_NAME}
|
||||
echo ::set-output name=REF_BRANCH::${REF_BRANCH}
|
||||
echo ::set-output name=REF_TAG::${REF_TAG}
|
||||
echo ::set-output name=REF_SHAS::${REF_SHAS}
|
||||
outputs REF_NAME REF_BRANCH REF_TAG REF_SHAS
|
||||
# parse target
|
||||
unset TARGET_ARCH
|
||||
case '${{ matrix.job.target }}' in
|
||||
|
@ -301,68 +296,50 @@ jobs:
|
|||
i686-*) TARGET_ARCH=i686 ;;
|
||||
x86_64-*) TARGET_ARCH=x86_64 ;;
|
||||
esac;
|
||||
echo set-output name=TARGET_ARCH::${TARGET_ARCH}
|
||||
echo ::set-output name=TARGET_ARCH::${TARGET_ARCH}
|
||||
unset TARGET_OS ; case '${{ matrix.job.target }}' in *-linux-*) TARGET_OS=linux ;; *-apple-*) TARGET_OS=macos ;; *-windows-*) TARGET_OS=windows ;; esac;
|
||||
echo set-output name=TARGET_OS::${TARGET_OS}
|
||||
echo ::set-output name=TARGET_OS::${TARGET_OS}
|
||||
outputs TARGET_ARCH TARGET_OS
|
||||
# package name
|
||||
PKG_suffix=".tar.gz" ; case '${{ matrix.job.target }}' in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
|
||||
PKG_BASENAME=${PROJECT_NAME}-${REF_TAG:-$REF_SHAS}-${{ matrix.job.target }}
|
||||
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
|
||||
echo set-output name=PKG_suffix::${PKG_suffix}
|
||||
echo set-output name=PKG_BASENAME::${PKG_BASENAME}
|
||||
echo set-output name=PKG_NAME::${PKG_NAME}
|
||||
echo ::set-output name=PKG_suffix::${PKG_suffix}
|
||||
echo ::set-output name=PKG_BASENAME::${PKG_BASENAME}
|
||||
echo ::set-output name=PKG_NAME::${PKG_NAME}
|
||||
outputs PKG_suffix PKG_BASENAME PKG_NAME
|
||||
# deployable tag? (ie, leading "vM" or "M"; M == version number)
|
||||
unset DEPLOY ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DEPLOY='true' ; fi
|
||||
echo set-output name=DEPLOY::${DEPLOY:-<empty>/false}
|
||||
echo ::set-output name=DEPLOY::${DEPLOY}
|
||||
outputs DEPLOY
|
||||
# DPKG architecture?
|
||||
unset DPKG_ARCH
|
||||
case ${{ matrix.job.target }} in
|
||||
x86_64-*-linux-*) DPKG_ARCH=amd64 ;;
|
||||
*-linux-*) DPKG_ARCH=${TARGET_ARCH} ;;
|
||||
esac
|
||||
echo set-output name=DPKG_ARCH::${DPKG_ARCH}
|
||||
echo ::set-output name=DPKG_ARCH::${DPKG_ARCH}
|
||||
outputs DPKG_ARCH
|
||||
# DPKG version?
|
||||
unset DPKG_VERSION ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DPKG_VERSION=${REF_TAG/#[vV]/} ; fi
|
||||
echo set-output name=DPKG_VERSION::${DPKG_VERSION}
|
||||
echo ::set-output name=DPKG_VERSION::${DPKG_VERSION}
|
||||
outputs DPKG_VERSION
|
||||
# DPKG base name/conflicts?
|
||||
DPKG_BASENAME=${PROJECT_NAME}
|
||||
DPKG_CONFLICTS=${PROJECT_NAME}-musl
|
||||
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac;
|
||||
echo set-output name=DPKG_BASENAME::${DPKG_BASENAME}
|
||||
echo set-output name=DPKG_CONFLICTS::${DPKG_CONFLICTS}
|
||||
echo ::set-output name=DPKG_BASENAME::${DPKG_BASENAME}
|
||||
echo ::set-output name=DPKG_CONFLICTS::${DPKG_CONFLICTS}
|
||||
outputs DPKG_BASENAME DPKG_CONFLICTS
|
||||
# DPKG name
|
||||
unset DPKG_NAME;
|
||||
if [[ -n $DPKG_ARCH && -n $DPKG_VERSION ]]; then DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" ; fi
|
||||
echo set-output name=DPKG_NAME::${DPKG_NAME}
|
||||
echo ::set-output name=DPKG_NAME::${DPKG_NAME}
|
||||
outputs DPKG_NAME
|
||||
# target-specific options
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='' ;
|
||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
outputs CARGO_FEATURES_OPTION
|
||||
# * CARGO_USE_CROSS (truthy)
|
||||
CARGO_USE_CROSS='true' ; case '${{ matrix.job.use-cross }}' in ''|0|f|false|n|no) unset CARGO_USE_CROSS ;; esac;
|
||||
echo set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS:-<empty>/false}
|
||||
echo ::set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS}
|
||||
outputs CARGO_USE_CROSS
|
||||
# ** pass needed environment into `cross` container (iff `cross` not already configured via "Cross.toml")
|
||||
if [ -n "${CARGO_USE_CROSS}" ] && [ ! -e "Cross.toml" ] ; then
|
||||
printf "[build.env]\npassthrough = [\"CI\"]\n" > Cross.toml
|
||||
fi
|
||||
# * test only library and/or binaries for arm-type targets
|
||||
unset CARGO_TEST_OPTIONS ; case '${{ matrix.job.target }}' in aarch64-* | arm-*) CARGO_TEST_OPTIONS="--bins" ;; esac;
|
||||
echo set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
|
||||
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
|
||||
outputs CARGO_TEST_OPTIONS
|
||||
# * executable for `strip`?
|
||||
STRIP="strip"
|
||||
case ${{ matrix.job.target }} in
|
||||
|
@ -370,8 +347,7 @@ jobs:
|
|||
arm-*-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;;
|
||||
*-pc-windows-msvc) STRIP="" ;;
|
||||
esac;
|
||||
echo set-output name=STRIP::${STRIP:-<empty>/false}
|
||||
echo ::set-output name=STRIP::${STRIP}
|
||||
outputs STRIP
|
||||
- name: Create all needed build/work directories
|
||||
shell: bash
|
||||
run: |
|
||||
|
@ -395,11 +371,12 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## Dependent VARs setup
|
||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||
# * determine sub-crate utility list
|
||||
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
||||
echo UTILITY_LIST=${UTILITY_LIST}
|
||||
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
||||
echo set-output name=UTILITY_LIST::${UTILITY_LIST}
|
||||
echo ::set-output name=CARGO_UTILITY_LIST_OPTIONS::${CARGO_UTILITY_LIST_OPTIONS}
|
||||
outputs CARGO_UTILITY_LIST_OPTIONS
|
||||
- name: Install `cargo-tree` # for dependency information
|
||||
uses: actions-rs/install@v0.1
|
||||
with:
|
||||
|
@ -524,34 +501,31 @@ jobs:
|
|||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
## VARs setup
|
||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||
# toolchain
|
||||
TOOLCHAIN="nightly-${{ env.RUST_COV_SRV }}" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support
|
||||
# * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files
|
||||
case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-gnu" ;; esac;
|
||||
# * use requested TOOLCHAIN if specified
|
||||
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
|
||||
echo set-output name=TOOLCHAIN::${TOOLCHAIN}
|
||||
echo ::set-output name=TOOLCHAIN::${TOOLCHAIN}
|
||||
outputs TOOLCHAIN
|
||||
# staging directory
|
||||
STAGING='_staging'
|
||||
echo set-output name=STAGING::${STAGING}
|
||||
echo ::set-output name=STAGING::${STAGING}
|
||||
outputs STAGING
|
||||
## # check for CODECOV_TOKEN availability (work-around for inaccessible 'secrets' object for 'if'; see <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
|
||||
## # note: CODECOV_TOKEN / HAS_CODECOV_TOKEN is not needed for public repositories when using AppVeyor, Azure Pipelines, CircleCI, GitHub Actions, Travis (see <https://docs.codecov.io/docs/about-the-codecov-bash-uploader#section-upload-token>)
|
||||
## unset HAS_CODECOV_TOKEN
|
||||
## if [ -n $CODECOV_TOKEN ]; then HAS_CODECOV_TOKEN='true' ; fi
|
||||
## echo set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN}
|
||||
## echo ::set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN}
|
||||
## outputs HAS_CODECOV_TOKEN
|
||||
# target-specific options
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='--all-features' ; ## default to '--all-features' for code coverage
|
||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
|
||||
outputs CARGO_FEATURES_OPTION
|
||||
# * CODECOV_FLAGS
|
||||
CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' )
|
||||
echo set-output name=CODECOV_FLAGS::${CODECOV_FLAGS}
|
||||
echo ::set-output name=CODECOV_FLAGS::${CODECOV_FLAGS}
|
||||
outputs CODECOV_FLAGS
|
||||
- name: rust toolchain ~ install
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
@ -563,11 +537,11 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
## Dependent VARs setup
|
||||
outputs() { for var in "$@" ; do echo steps.vars.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
||||
# * determine sub-crate utility list
|
||||
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
||||
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
||||
echo set-output name=UTILITY_LIST::${UTILITY_LIST}
|
||||
echo ::set-output name=CARGO_UTILITY_LIST_OPTIONS::${CARGO_UTILITY_LIST_OPTIONS}
|
||||
outputs CARGO_UTILITY_LIST_OPTIONS
|
||||
- name: Test uucore
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
|
@ -606,7 +580,7 @@ jobs:
|
|||
with:
|
||||
crate: grcov
|
||||
version: latest
|
||||
use-tool-cache: true
|
||||
use-tool-cache: false
|
||||
- name: Generate coverage data (via `grcov`)
|
||||
id: coverage
|
||||
shell: bash
|
||||
|
|
|
@ -97,6 +97,9 @@ Michael Debertol
|
|||
Michael Gehring
|
||||
Michael
|
||||
Gehring
|
||||
Mitchell Mebane
|
||||
Mitchell
|
||||
Mebane
|
||||
Morten Olsen Lysgaard
|
||||
Morten
|
||||
Olsen
|
||||
|
|
|
@ -7,6 +7,7 @@ advapi
|
|||
advapi32-sys
|
||||
aho-corasick
|
||||
backtrace
|
||||
blake2b_simd
|
||||
bstr
|
||||
byteorder
|
||||
chacha
|
||||
|
@ -47,6 +48,7 @@ xattr
|
|||
# * rust/rustc
|
||||
RUSTDOCFLAGS
|
||||
RUSTFLAGS
|
||||
bitor # BitOr trait function
|
||||
bitxor # BitXor trait function
|
||||
clippy
|
||||
concat
|
||||
|
|
48
Cargo.lock
generated
48
Cargo.lock
generated
|
@ -44,13 +44,16 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
]
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
|
@ -100,11 +103,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "blake2-rfc"
|
||||
version = "0.2.18"
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
|
||||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
@ -700,9 +704,9 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"
|
|||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
@ -1383,12 +1387,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
|
@ -1501,9 +1502,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -1771,6 +1772,7 @@ dependencies = [
|
|||
name = "uu_cat"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"nix 0.20.0",
|
||||
"thiserror",
|
||||
|
@ -1871,6 +1873,7 @@ dependencies = [
|
|||
name = "uu_cut"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bstr",
|
||||
"clap",
|
||||
"memchr 2.4.0",
|
||||
|
@ -1904,6 +1907,7 @@ dependencies = [
|
|||
name = "uu_dircolors"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"glob 0.3.0",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
@ -2028,7 +2032,7 @@ dependencies = [
|
|||
name = "uu_hashsum"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"blake2-rfc",
|
||||
"blake2b_simd",
|
||||
"clap",
|
||||
"digest",
|
||||
"hex",
|
||||
|
@ -2215,6 +2219,8 @@ dependencies = [
|
|||
"nix 0.13.1",
|
||||
"redox_syscall 0.1.57",
|
||||
"redox_termios",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
|
@ -2258,6 +2264,7 @@ dependencies = [
|
|||
name = "uu_nohup"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
|
@ -2416,6 +2423,7 @@ dependencies = [
|
|||
"uucore",
|
||||
"uucore_procs",
|
||||
"walkdir",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2597,7 +2605,6 @@ name = "uu_timeout"
|
|||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"getopts",
|
||||
"libc",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
@ -2655,6 +2662,7 @@ dependencies = [
|
|||
name = "uu_tty"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
|
|
|
@ -349,9 +349,9 @@ sha1 = { version="0.6", features=["std"] }
|
|||
tempfile = "3.2.0"
|
||||
time = "0.1"
|
||||
unindent = "0.1"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="src/uucore", features=["entries"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="src/uucore", features=["entries", "process"] }
|
||||
walkdir = "2.2"
|
||||
atty = "0.2.14"
|
||||
atty = "0.2"
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
rlimit = "0.4.0"
|
||||
|
|
16
GNUmakefile
16
GNUmakefile
|
@ -268,11 +268,11 @@ test:
|
|||
${CARGO} test ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE)" --no-default-features $(TEST_NO_FAIL_FAST)
|
||||
|
||||
busybox-src:
|
||||
if [ ! -e $(BUSYBOX_SRC) ]; then \
|
||||
mkdir -p $(BUSYBOX_ROOT); \
|
||||
wget https://busybox.net/downloads/busybox-$(BUSYBOX_VER).tar.bz2 -P $(BUSYBOX_ROOT); \
|
||||
tar -C $(BUSYBOX_ROOT) -xf $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER).tar.bz2; \
|
||||
fi; \
|
||||
if [ ! -e "$(BUSYBOX_SRC)" ] ; then \
|
||||
mkdir -p "$(BUSYBOX_ROOT)" ; \
|
||||
wget "https://busybox.net/downloads/busybox-$(BUSYBOX_VER).tar.bz2" -P "$(BUSYBOX_ROOT)" ; \
|
||||
tar -C "$(BUSYBOX_ROOT)" -xf "$(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER).tar.bz2" ; \
|
||||
fi ;
|
||||
|
||||
# This is a busybox-specific config file their test suite wants to parse.
|
||||
$(BUILDDIR)/.config: $(BASEDIR)/.busybox-config
|
||||
|
@ -280,10 +280,12 @@ $(BUILDDIR)/.config: $(BASEDIR)/.busybox-config
|
|||
|
||||
# Test under the busybox test suite
|
||||
$(BUILDDIR)/busybox: busybox-src build-coreutils $(BUILDDIR)/.config
|
||||
cp $(BUILDDIR)/coreutils $(BUILDDIR)/busybox; \
|
||||
chmod +x $@;
|
||||
cp "$(BUILDDIR)/coreutils" "$(BUILDDIR)/busybox"
|
||||
chmod +x $@
|
||||
|
||||
prepare-busytest: $(BUILDDIR)/busybox
|
||||
# disable inapplicable tests
|
||||
-( cd "$(BUSYBOX_SRC)/testsuite" ; if [ -e "busybox.tests" ] ; then mv busybox.tests busybox.tests- ; fi ; )
|
||||
|
||||
ifeq ($(EXES),)
|
||||
busytest:
|
||||
|
|
42
README.md
42
README.md
|
@ -342,22 +342,22 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
|||
| Done | Semi-Done | To Do |
|
||||
|-----------|-----------|--------|
|
||||
| arch | cp | chcon |
|
||||
| base32 | expr | csplit |
|
||||
| base64 | install | dd |
|
||||
| basename | ls | df |
|
||||
| cat | more | numfmt |
|
||||
| chgrp | od (`--strings` and 128-bit data types missing) | runcon |
|
||||
| chmod | printf | stty |
|
||||
| chown | sort | |
|
||||
| chroot | split | |
|
||||
| cksum | tail | |
|
||||
| comm | test | |
|
||||
| csplit | date | |
|
||||
| cut | join | |
|
||||
| dircolors | df | |
|
||||
| base32 | date | dd |
|
||||
| base64 | df | runcon |
|
||||
| basename | expr | stty |
|
||||
| cat | install | |
|
||||
| chgrp | join | |
|
||||
| chmod | ls | |
|
||||
| chown | more | |
|
||||
| chroot | numfmt | |
|
||||
| cksum | od (`--strings` and 128-bit data types missing) | |
|
||||
| comm | pr | |
|
||||
| csplit | printf | |
|
||||
| cut | sort | |
|
||||
| dircolors | split | |
|
||||
| dirname | tac | |
|
||||
| du | pr | |
|
||||
| echo | | |
|
||||
| du | tail | |
|
||||
| echo | test | |
|
||||
| env | | |
|
||||
| expand | | |
|
||||
| factor | | |
|
||||
|
@ -374,12 +374,12 @@ To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
|||
| link | | |
|
||||
| ln | | |
|
||||
| logname | | |
|
||||
| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | |
|
||||
| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/uu/hashsum/src/hashsum.rs)) | | |
|
||||
| mkdir | | |
|
||||
| mkfifo | | |
|
||||
| mknod | | |
|
||||
|
|
|
@ -38,18 +38,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
let config_result: Result<base_common::Config, String> =
|
||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
||||
|
||||
if config_result.is_err() {
|
||||
match config_result {
|
||||
Ok(_) => panic!(),
|
||||
Err(s) => crash!(BASE_CMD_PARSE_ERROR, "{}", s),
|
||||
}
|
||||
}
|
||||
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
||||
|
||||
// Create a reference to stdin so we can return a locked stdin from
|
||||
// parse_base_cmd_args
|
||||
let stdin_raw = stdin();
|
||||
let config = config_result.unwrap();
|
||||
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw);
|
||||
|
||||
base_common::handle_input(
|
||||
|
|
|
@ -38,18 +38,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let name = executable!();
|
||||
let config_result: Result<base_common::Config, String> =
|
||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
||||
|
||||
if config_result.is_err() {
|
||||
match config_result {
|
||||
Ok(_) => panic!(),
|
||||
Err(s) => crash!(BASE_CMD_PARSE_ERROR, "{}", s),
|
||||
}
|
||||
}
|
||||
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
|
||||
|
||||
// Create a reference to stdin so we can return a locked stdin from
|
||||
// parse_base_cmd_args
|
||||
let stdin_raw = stdin();
|
||||
let config = config_result.unwrap();
|
||||
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw);
|
||||
|
||||
base_common::handle_input(
|
||||
|
|
|
@ -110,7 +110,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
let line_ending = if opt_zero { "\0" } else { "\n" };
|
||||
for path in paths {
|
||||
print!("{}{}", basename(&path, &suffix), line_ending);
|
||||
print!("{}{}", basename(path, suffix), line_ending);
|
||||
}
|
||||
|
||||
0
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/cat.rs"
|
|||
[dependencies]
|
||||
clap = "2.33"
|
||||
thiserror = "1.0"
|
||||
atty = "0.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ use clap::{crate_version, App, Arg};
|
|||
use std::fs::{metadata, File};
|
||||
use std::io::{self, Read, Write};
|
||||
use thiserror::Error;
|
||||
use uucore::fs::is_stdin_interactive;
|
||||
|
||||
/// Linux splice support
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
|
@ -295,7 +294,7 @@ fn cat_handle<R: Read>(
|
|||
if options.can_write_fast() {
|
||||
write_fast(handle)
|
||||
} else {
|
||||
write_lines(handle, &options, state)
|
||||
write_lines(handle, options, state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,9 +305,9 @@ fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> Cat
|
|||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
file_descriptor: stdin.as_raw_fd(),
|
||||
reader: stdin,
|
||||
is_interactive: is_stdin_interactive(),
|
||||
is_interactive: atty::is(atty::Stream::Stdin),
|
||||
};
|
||||
return cat_handle(&mut handle, &options, state);
|
||||
return cat_handle(&mut handle, options, state);
|
||||
}
|
||||
match get_input_type(path)? {
|
||||
InputType::Directory => Err(CatError::IsDirectory),
|
||||
|
@ -322,7 +321,7 @@ fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> Cat
|
|||
reader: socket,
|
||||
is_interactive: false,
|
||||
};
|
||||
cat_handle(&mut handle, &options, state)
|
||||
cat_handle(&mut handle, options, state)
|
||||
}
|
||||
_ => {
|
||||
let file = File::open(path)?;
|
||||
|
@ -332,7 +331,7 @@ fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> Cat
|
|||
reader: file,
|
||||
is_interactive: false,
|
||||
};
|
||||
cat_handle(&mut handle, &options, state)
|
||||
cat_handle(&mut handle, options, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -345,7 +344,7 @@ fn cat_files(files: Vec<String>, options: &OutputOptions) -> Result<(), u32> {
|
|||
};
|
||||
|
||||
for path in &files {
|
||||
if let Err(err) = cat_path(path, &options, &mut state) {
|
||||
if let Err(err) = cat_path(path, options, &mut state) {
|
||||
show_error!("{}: {}", path, err);
|
||||
error_count += 1;
|
||||
}
|
||||
|
|
|
@ -127,31 +127,32 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let verbose = matches.is_present(options::VERBOSE);
|
||||
let preserve_root = matches.is_present(options::PRESERVE_ROOT);
|
||||
let recursive = matches.is_present(options::RECURSIVE);
|
||||
let fmode =
|
||||
matches
|
||||
.value_of(options::REFERENCE)
|
||||
.and_then(|ref fref| match fs::metadata(fref) {
|
||||
Ok(meta) => Some(meta.mode()),
|
||||
Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
|
||||
});
|
||||
let fmode = matches
|
||||
.value_of(options::REFERENCE)
|
||||
.and_then(|fref| match fs::metadata(fref) {
|
||||
Ok(meta) => Some(meta.mode()),
|
||||
Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err),
|
||||
});
|
||||
let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
|
||||
let mut cmode = if mode_had_minus_prefix {
|
||||
let cmode = if mode_had_minus_prefix {
|
||||
// clap parsing is finished, now put prefix back
|
||||
Some(format!("-{}", modes))
|
||||
format!("-{}", modes)
|
||||
} else {
|
||||
Some(modes.to_string())
|
||||
modes.to_string()
|
||||
};
|
||||
let mut files: Vec<String> = matches
|
||||
.values_of(options::FILE)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
.unwrap_or_default();
|
||||
if fmode.is_some() {
|
||||
let cmode = if fmode.is_some() {
|
||||
// "--reference" and MODE are mutually exclusive
|
||||
// if "--reference" was used MODE needs to be interpreted as another FILE
|
||||
// it wasn't possible to implement this behavior directly with clap
|
||||
files.push(cmode.unwrap());
|
||||
cmode = None;
|
||||
}
|
||||
files.push(cmode);
|
||||
None
|
||||
} else {
|
||||
Some(cmode)
|
||||
};
|
||||
|
||||
let chmoder = Chmoder {
|
||||
changes,
|
||||
|
@ -230,11 +231,11 @@ impl Chmoder {
|
|||
return Err(1);
|
||||
}
|
||||
if !self.recursive {
|
||||
r = self.chmod_file(&file).and(r);
|
||||
r = self.chmod_file(file).and(r);
|
||||
} else {
|
||||
for entry in WalkDir::new(&filename).into_iter().filter_map(|e| e.ok()) {
|
||||
let file = entry.path();
|
||||
r = self.chmod_file(&file).and(r);
|
||||
r = self.chmod_file(file).and(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,7 +220,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
};
|
||||
|
||||
let filter = if let Some(spec) = matches.value_of(options::FROM) {
|
||||
match parse_spec(&spec) {
|
||||
match parse_spec(spec) {
|
||||
Ok((Some(uid), None)) => IfFrom::User(uid),
|
||||
Ok((None, Some(gid))) => IfFrom::Group(gid),
|
||||
Ok((Some(uid), Some(gid))) => IfFrom::UserGroup(uid, gid),
|
||||
|
@ -248,7 +248,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
match parse_spec(&owner) {
|
||||
match parse_spec(owner) {
|
||||
Ok((u, g)) => {
|
||||
dest_uid = u;
|
||||
dest_gid = g;
|
||||
|
|
|
@ -106,13 +106,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let mut vector: Vec<&str> = Vec::new();
|
||||
for (&k, v) in matches.args.iter() {
|
||||
vector.push(k);
|
||||
vector.push(&v.vals[0].to_str().unwrap());
|
||||
vector.push(v.vals[0].to_str().unwrap());
|
||||
}
|
||||
vector
|
||||
}
|
||||
};
|
||||
|
||||
set_context(&newroot, &matches);
|
||||
set_context(newroot, &matches);
|
||||
|
||||
let pstatus = Command::new(command[0])
|
||||
.args(&command[1..])
|
||||
|
@ -132,7 +132,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
|
|||
let group_str = options.value_of(options::GROUP).unwrap_or_default();
|
||||
let groups_str = options.value_of(options::GROUPS).unwrap_or_default();
|
||||
let userspec = match userspec_str {
|
||||
Some(ref u) => {
|
||||
Some(u) => {
|
||||
let s: Vec<&str> = u.split(':').collect();
|
||||
if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
|
||||
crash!(1, "invalid userspec: `{}`", u)
|
||||
|
|
|
@ -1088,7 +1088,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
|
|||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[allow(clippy::unnecessary_unwrap)] // needed for windows version
|
||||
#[allow(clippy::unnecessary_wraps)] // needed for windows version
|
||||
fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> {
|
||||
match std::os::unix::fs::symlink(source, dest).context(context) {
|
||||
Ok(_) => Ok(()),
|
||||
|
|
|
@ -92,7 +92,7 @@ where
|
|||
T: BufRead,
|
||||
{
|
||||
let mut input_iter = InputSplitter::new(input.lines().enumerate());
|
||||
let mut split_writer = SplitWriter::new(&options);
|
||||
let mut split_writer = SplitWriter::new(options);
|
||||
let ret = do_csplit(&mut split_writer, patterns, &mut input_iter);
|
||||
|
||||
// consume the rest
|
||||
|
|
|
@ -20,6 +20,7 @@ uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
|||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
memchr = "2"
|
||||
bstr = "0.2"
|
||||
atty = "0.2"
|
||||
|
||||
[[bin]]
|
||||
name = "cut"
|
||||
|
|
|
@ -17,7 +17,6 @@ use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
|
|||
use std::path::Path;
|
||||
|
||||
use self::searcher::Searcher;
|
||||
use uucore::fs::is_stdout_interactive;
|
||||
use uucore::ranges::Range;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
|
@ -127,7 +126,7 @@ enum Mode {
|
|||
}
|
||||
|
||||
fn stdout_writer() -> Box<dyn Write> {
|
||||
if is_stdout_interactive() {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(BufWriter::new(stdout())) as Box<dyn Write>
|
||||
|
|
|
@ -15,6 +15,7 @@ edition = "2018"
|
|||
path = "src/dircolors.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
glob = "0.3.0"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
||||
// (c) Mitchell Mebane <mitchell.mebane@gmail.com>
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
@ -15,6 +16,15 @@ use std::env;
|
|||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
|
||||
mod options {
|
||||
pub const BOURNE_SHELL: &str = "bourne-shell";
|
||||
pub const C_SHELL: &str = "c-shell";
|
||||
pub const PRINT_DATABASE: &str = "print-database";
|
||||
pub const FILE: &str = "FILE";
|
||||
}
|
||||
|
||||
static SYNTAX: &str = "[OPTION]... [FILE]";
|
||||
static SUMMARY: &str = "Output commands to set the LS_COLORS environment variable.";
|
||||
static LONG_HELP: &str = "
|
||||
|
@ -52,28 +62,56 @@ pub fn guess_syntax() -> OutputFmt {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} {1}", executable!(), SYNTAX)
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let args = args
|
||||
.collect_str(InvalidEncodingHandling::Ignore)
|
||||
.accept_any();
|
||||
|
||||
let matches = app!(SYNTAX, SUMMARY, LONG_HELP)
|
||||
.optflag("b", "sh", "output Bourne shell code to set LS_COLORS")
|
||||
.optflag(
|
||||
"",
|
||||
"bourne-shell",
|
||||
"output Bourne shell code to set LS_COLORS",
|
||||
)
|
||||
.optflag("c", "csh", "output C shell code to set LS_COLORS")
|
||||
.optflag("", "c-shell", "output C shell code to set LS_COLORS")
|
||||
.optflag("p", "print-database", "print the byte counts")
|
||||
.parse(args);
|
||||
let usage = get_usage();
|
||||
|
||||
if (matches.opt_present("csh")
|
||||
|| matches.opt_present("c-shell")
|
||||
|| matches.opt_present("sh")
|
||||
|| matches.opt_present("bourne-shell"))
|
||||
&& matches.opt_present("print-database")
|
||||
let matches = App::new(executable!())
|
||||
.version(crate_version!())
|
||||
.about(SUMMARY)
|
||||
.usage(&usage[..])
|
||||
.after_help(LONG_HELP)
|
||||
.arg(
|
||||
Arg::with_name(options::BOURNE_SHELL)
|
||||
.long("sh")
|
||||
.short("b")
|
||||
.visible_alias("bourne-shell")
|
||||
.help("output Bourne shell code to set LS_COLORS")
|
||||
.display_order(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::C_SHELL)
|
||||
.long("csh")
|
||||
.short("c")
|
||||
.visible_alias("c-shell")
|
||||
.help("output C shell code to set LS_COLORS")
|
||||
.display_order(2),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::PRINT_DATABASE)
|
||||
.long("print-database")
|
||||
.short("p")
|
||||
.help("print the byte counts")
|
||||
.display_order(3),
|
||||
)
|
||||
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
||||
.get_matches_from(&args);
|
||||
|
||||
let files = matches
|
||||
.values_of(options::FILE)
|
||||
.map_or(vec![], |file_values| file_values.collect());
|
||||
|
||||
// clap provides .conflicts_with / .conflicts_with_all, but we want to
|
||||
// manually handle conflicts so we can match the output of GNU coreutils
|
||||
if (matches.is_present(options::C_SHELL) || matches.is_present(options::BOURNE_SHELL))
|
||||
&& matches.is_present(options::PRINT_DATABASE)
|
||||
{
|
||||
show_usage_error!(
|
||||
"the options to output dircolors' internal database and\nto select a shell \
|
||||
|
@ -82,12 +120,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
return 1;
|
||||
}
|
||||
|
||||
if matches.opt_present("print-database") {
|
||||
if !matches.free.is_empty() {
|
||||
if matches.is_present(options::PRINT_DATABASE) {
|
||||
if !files.is_empty() {
|
||||
show_usage_error!(
|
||||
"extra operand ‘{}’\nfile operands cannot be combined with \
|
||||
--print-database (-p)",
|
||||
matches.free[0]
|
||||
files[0]
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
@ -96,9 +134,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
let mut out_format = OutputFmt::Unknown;
|
||||
if matches.opt_present("csh") || matches.opt_present("c-shell") {
|
||||
if matches.is_present(options::C_SHELL) {
|
||||
out_format = OutputFmt::CShell;
|
||||
} else if matches.opt_present("sh") || matches.opt_present("bourne-shell") {
|
||||
} else if matches.is_present(options::BOURNE_SHELL) {
|
||||
out_format = OutputFmt::Shell;
|
||||
}
|
||||
|
||||
|
@ -113,24 +151,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
let result;
|
||||
if matches.free.is_empty() {
|
||||
if files.is_empty() {
|
||||
result = parse(INTERNAL_DB.lines(), out_format, "")
|
||||
} else {
|
||||
if matches.free.len() > 1 {
|
||||
show_usage_error!("extra operand ‘{}’", matches.free[1]);
|
||||
if files.len() > 1 {
|
||||
show_usage_error!("extra operand ‘{}’", files[1]);
|
||||
return 1;
|
||||
}
|
||||
match File::open(matches.free[0].as_str()) {
|
||||
match File::open(files[0]) {
|
||||
Ok(f) => {
|
||||
let fin = BufReader::new(f);
|
||||
result = parse(
|
||||
fin.lines().filter_map(Result::ok),
|
||||
out_format,
|
||||
matches.free[0].as_str(),
|
||||
)
|
||||
result = parse(fin.lines().filter_map(Result::ok), out_format, files[0])
|
||||
}
|
||||
Err(e) => {
|
||||
show_error!("{}: {}", matches.free[0], e);
|
||||
show_error!("{}: {}", files[0], e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ use std::os::unix::fs::MetadataExt;
|
|||
use std::os::windows::fs::MetadataExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
#[cfg(windows)]
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
@ -159,7 +161,7 @@ fn birth_u64(meta: &Metadata) -> Option<u64> {
|
|||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_size_on_disk(path: &PathBuf) -> u64 {
|
||||
fn get_size_on_disk(path: &Path) -> u64 {
|
||||
let mut size_on_disk = 0;
|
||||
|
||||
// bind file so it stays in scope until end of function
|
||||
|
@ -191,7 +193,7 @@ fn get_size_on_disk(path: &PathBuf) -> u64 {
|
|||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_file_info(path: &PathBuf) -> Option<FileInfo> {
|
||||
fn get_file_info(path: &Path) -> Option<FileInfo> {
|
||||
let mut result = None;
|
||||
|
||||
let file = match fs::File::open(path) {
|
||||
|
@ -256,7 +258,7 @@ fn unit_string_to_number(s: &str) -> Option<u64> {
|
|||
|
||||
fn translate_to_pure_number(s: &Option<&str>) -> Option<u64> {
|
||||
match *s {
|
||||
Some(ref s) => unit_string_to_number(s),
|
||||
Some(s) => unit_string_to_number(s),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
@ -318,8 +320,7 @@ fn du(
|
|||
if this_stat.is_dir {
|
||||
futures.push(du(this_stat, options, depth + 1, inodes));
|
||||
} else {
|
||||
if this_stat.inode.is_some() {
|
||||
let inode = this_stat.inode.unwrap();
|
||||
if let Some(inode) = this_stat.inode {
|
||||
if inodes.contains(&inode) {
|
||||
continue;
|
||||
}
|
||||
|
@ -358,7 +359,9 @@ fn du(
|
|||
my_stat.size += stat.size;
|
||||
my_stat.blocks += stat.blocks;
|
||||
}
|
||||
options.max_depth == None || depth < options.max_depth.unwrap()
|
||||
options
|
||||
.max_depth
|
||||
.map_or(true, |max_depth| depth < max_depth)
|
||||
}));
|
||||
stats.push(my_stat);
|
||||
Box::new(stats.into_iter())
|
||||
|
@ -431,6 +434,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
although the apparent size is usually smaller, it may be larger due to holes \
|
||||
in ('sparse') files, internal fragmentation, indirect blocks, and the like"
|
||||
)
|
||||
.alias("app") // The GNU test suite uses this alias
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::BLOCK_SIZE)
|
||||
|
@ -582,12 +586,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let max_depth_str = matches.value_of(options::MAX_DEPTH);
|
||||
let max_depth = max_depth_str.as_ref().and_then(|s| s.parse::<usize>().ok());
|
||||
match (max_depth_str, max_depth) {
|
||||
(Some(ref s), _) if summarize => {
|
||||
show_error!("summarizing conflicts with --max-depth={}", *s);
|
||||
(Some(s), _) if summarize => {
|
||||
show_error!("summarizing conflicts with --max-depth={}", s);
|
||||
return 1;
|
||||
}
|
||||
(Some(ref s), None) => {
|
||||
show_error!("invalid maximum depth '{}'", *s);
|
||||
(Some(s), None) => {
|
||||
show_error!("invalid maximum depth '{}'", s);
|
||||
return 1;
|
||||
}
|
||||
(Some(_), Some(_)) | (None, _) => { /* valid */ }
|
||||
|
|
|
@ -181,7 +181,7 @@ fn execute(no_newline: bool, escaped: bool, free: Vec<String>) -> io::Result<()>
|
|||
write!(output, " ")?;
|
||||
}
|
||||
if escaped {
|
||||
let should_stop = print_escaped(&input, &mut output)?;
|
||||
let should_stop = print_escaped(input, &mut output)?;
|
||||
if should_stop {
|
||||
break;
|
||||
}
|
||||
|
|
2
src/uu/env/src/env.rs
vendored
2
src/uu/env/src/env.rs
vendored
|
@ -245,7 +245,7 @@ fn run_env(args: impl uucore::Args) -> Result<(), i32> {
|
|||
}
|
||||
|
||||
// set specified env vars
|
||||
for &(ref name, ref val) in &opts.sets {
|
||||
for &(name, val) in &opts.sets {
|
||||
// FIXME: set_var() panics if name is an empty string
|
||||
env::set_var(name, val);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ extern crate uucore;
|
|||
use clap::{crate_version, App, Arg, ArgMatches};
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
|
||||
use std::iter::repeat;
|
||||
use std::str::from_utf8;
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
|
@ -90,7 +89,7 @@ impl Options {
|
|||
})
|
||||
.max()
|
||||
.unwrap(); // length of tabstops is guaranteed >= 1
|
||||
let tspaces = repeat(' ').take(nspaces).collect();
|
||||
let tspaces = " ".repeat(nspaces);
|
||||
|
||||
let files: Vec<String> = match matches.values_of(options::FILES) {
|
||||
Some(s) => s.map(|v| v.to_string()).collect(),
|
||||
|
@ -236,7 +235,7 @@ fn expand(options: Options) {
|
|||
|
||||
// now dump out either spaces if we're expanding, or a literal tab if we're not
|
||||
if init || !options.iflag {
|
||||
safe_unwrap!(output.write_all(&options.tspaces[..nts].as_bytes()));
|
||||
safe_unwrap!(output.write_all(options.tspaces[..nts].as_bytes()));
|
||||
} else {
|
||||
safe_unwrap!(output.write_all(&buf[byte..byte + nbytes]));
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
fn process_expr(token_strings: &[String]) -> Result<String, String> {
|
||||
let maybe_tokens = tokens::strings_to_tokens(&token_strings);
|
||||
let maybe_tokens = tokens::strings_to_tokens(token_strings);
|
||||
let maybe_ast = syntax_tree::tokens_to_ast(maybe_tokens);
|
||||
evaluate_ast(maybe_ast)
|
||||
}
|
||||
|
@ -56,11 +56,7 @@ fn print_expr_error(expr_error: &str) -> ! {
|
|||
}
|
||||
|
||||
fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::AstNode>, String>) -> Result<String, String> {
|
||||
if maybe_ast.is_err() {
|
||||
Err(maybe_ast.err().unwrap())
|
||||
} else {
|
||||
maybe_ast.ok().unwrap().evaluate()
|
||||
}
|
||||
maybe_ast.and_then(|ast| ast.evaluate())
|
||||
}
|
||||
|
||||
fn maybe_handle_help_or_version(args: &[String]) -> bool {
|
||||
|
|
|
@ -175,23 +175,14 @@ impl AstNode {
|
|||
pub fn tokens_to_ast(
|
||||
maybe_tokens: Result<Vec<(usize, Token)>, String>,
|
||||
) -> Result<Box<AstNode>, String> {
|
||||
if maybe_tokens.is_err() {
|
||||
Err(maybe_tokens.err().unwrap())
|
||||
} else {
|
||||
let tokens = maybe_tokens.ok().unwrap();
|
||||
maybe_tokens.and_then(|tokens| {
|
||||
let mut out_stack: TokenStack = Vec::new();
|
||||
let mut op_stack: TokenStack = Vec::new();
|
||||
|
||||
for (token_idx, token) in tokens {
|
||||
if let Err(reason) =
|
||||
push_token_to_either_stack(token_idx, &token, &mut out_stack, &mut op_stack)
|
||||
{
|
||||
return Err(reason);
|
||||
}
|
||||
}
|
||||
if let Err(reason) = move_rest_of_ops_to_out(&mut out_stack, &mut op_stack) {
|
||||
return Err(reason);
|
||||
push_token_to_either_stack(token_idx, &token, &mut out_stack, &mut op_stack)?;
|
||||
}
|
||||
move_rest_of_ops_to_out(&mut out_stack, &mut op_stack)?;
|
||||
assert!(op_stack.is_empty());
|
||||
|
||||
maybe_dump_rpn(&out_stack);
|
||||
|
@ -205,7 +196,7 @@ pub fn tokens_to_ast(
|
|||
maybe_dump_ast(&result);
|
||||
result
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn maybe_dump_ast(result: &Result<Box<AstNode>, String>) {
|
||||
|
|
|
@ -78,27 +78,27 @@ pub fn strings_to_tokens(strings: &[String]) -> Result<Vec<(usize, Token)>, Stri
|
|||
"(" => Token::ParOpen,
|
||||
")" => Token::ParClose,
|
||||
|
||||
"^" => Token::new_infix_op(&s, false, 7),
|
||||
"^" => Token::new_infix_op(s, false, 7),
|
||||
|
||||
":" => Token::new_infix_op(&s, true, 6),
|
||||
":" => Token::new_infix_op(s, true, 6),
|
||||
|
||||
"*" => Token::new_infix_op(&s, true, 5),
|
||||
"/" => Token::new_infix_op(&s, true, 5),
|
||||
"%" => Token::new_infix_op(&s, true, 5),
|
||||
"*" => Token::new_infix_op(s, true, 5),
|
||||
"/" => Token::new_infix_op(s, true, 5),
|
||||
"%" => Token::new_infix_op(s, true, 5),
|
||||
|
||||
"+" => Token::new_infix_op(&s, true, 4),
|
||||
"-" => Token::new_infix_op(&s, true, 4),
|
||||
"+" => Token::new_infix_op(s, true, 4),
|
||||
"-" => Token::new_infix_op(s, true, 4),
|
||||
|
||||
"=" => Token::new_infix_op(&s, true, 3),
|
||||
"!=" => Token::new_infix_op(&s, true, 3),
|
||||
"<" => Token::new_infix_op(&s, true, 3),
|
||||
">" => Token::new_infix_op(&s, true, 3),
|
||||
"<=" => Token::new_infix_op(&s, true, 3),
|
||||
">=" => Token::new_infix_op(&s, true, 3),
|
||||
"=" => Token::new_infix_op(s, true, 3),
|
||||
"!=" => Token::new_infix_op(s, true, 3),
|
||||
"<" => Token::new_infix_op(s, true, 3),
|
||||
">" => Token::new_infix_op(s, true, 3),
|
||||
"<=" => Token::new_infix_op(s, true, 3),
|
||||
">=" => Token::new_infix_op(s, true, 3),
|
||||
|
||||
"&" => Token::new_infix_op(&s, true, 2),
|
||||
"&" => Token::new_infix_op(s, true, 2),
|
||||
|
||||
"|" => Token::new_infix_op(&s, true, 1),
|
||||
"|" => Token::new_infix_op(s, true, 1),
|
||||
|
||||
"match" => Token::PrefixOp {
|
||||
arity: 2,
|
||||
|
@ -117,9 +117,9 @@ pub fn strings_to_tokens(strings: &[String]) -> Result<Vec<(usize, Token)>, Stri
|
|||
value: s.clone(),
|
||||
},
|
||||
|
||||
_ => Token::new_value(&s),
|
||||
_ => Token::new_value(s),
|
||||
};
|
||||
push_token_if_not_escaped(&mut tokens_acc, tok_idx, token_if_not_escaped, &s);
|
||||
push_token_if_not_escaped(&mut tokens_acc, tok_idx, token_if_not_escaped, s);
|
||||
tok_idx += 1;
|
||||
}
|
||||
maybe_dump_tokens_acc(&tokens_acc);
|
||||
|
|
|
@ -98,7 +98,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
let slice = &arg;
|
||||
if slice.starts_with('-') && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
|
||||
if slice.starts_with('-') && slice.chars().nth(1).map_or(false, |c| c.is_digit(10)) {
|
||||
let mut v = args.to_vec();
|
||||
v.remove(i);
|
||||
return (v, Some(slice[1..].to_owned()));
|
||||
|
@ -109,7 +109,7 @@ fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
|
|||
|
||||
fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: usize) {
|
||||
for filename in &filenames {
|
||||
let filename: &str = &filename;
|
||||
let filename: &str = filename;
|
||||
let mut stdin_buf;
|
||||
let mut file_buf;
|
||||
let buffer = BufReader::new(if filename == "-" {
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/groups.rs"
|
||||
|
||||
[dependencies]
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "process"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
clap = "2.33"
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
use uucore::entries::{get_groups, gid2grp, Locate, Passwd};
|
||||
use uucore::entries::{get_groups_gnu, gid2grp, Locate, Passwd};
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
|
||||
|
@ -35,7 +35,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
None => {
|
||||
println!(
|
||||
"{}",
|
||||
get_groups()
|
||||
get_groups_gnu(None)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|&g| gid2grp(g).unwrap())
|
||||
|
|
9
src/uu/hashsum/BENCHMARKING.md
Normal file
9
src/uu/hashsum/BENCHMARKING.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
## Benchmarking hashsum
|
||||
|
||||
### To bench blake2
|
||||
|
||||
Taken from: https://github.com/uutils/coreutils/pull/2296
|
||||
|
||||
With a large file:
|
||||
$ hyperfine "./target/release/coreutils hashsum --b2sum large-file" "b2sum large-file"
|
||||
|
|
@ -25,7 +25,7 @@ regex-syntax = "0.6.7"
|
|||
sha1 = "0.6.0"
|
||||
sha2 = "0.6.0"
|
||||
sha3 = "0.6.0"
|
||||
blake2-rfc = "0.2.18"
|
||||
blake2b_simd = "0.5.11"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
extern crate blake2_rfc;
|
||||
extern crate digest;
|
||||
extern crate md5;
|
||||
extern crate sha1;
|
||||
|
@ -49,9 +48,9 @@ impl Digest for md5::Context {
|
|||
}
|
||||
}
|
||||
|
||||
impl Digest for blake2_rfc::blake2b::Blake2b {
|
||||
impl Digest for blake2b_simd::State {
|
||||
fn new() -> Self {
|
||||
blake2_rfc::blake2b::Blake2b::new(64)
|
||||
Self::new()
|
||||
}
|
||||
|
||||
fn input(&mut self, input: &[u8]) {
|
||||
|
@ -59,12 +58,12 @@ impl Digest for blake2_rfc::blake2b::Blake2b {
|
|||
}
|
||||
|
||||
fn result(&mut self, out: &mut [u8]) {
|
||||
let hash_result = &self.clone().finalize();
|
||||
out.copy_from_slice(&hash_result.as_bytes());
|
||||
let hash_result = &self.finalize();
|
||||
out.copy_from_slice(hash_result.as_bytes());
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
*self = blake2_rfc::blake2b::Blake2b::new(64);
|
||||
*self = Self::new();
|
||||
}
|
||||
|
||||
fn output_bits(&self) -> usize {
|
||||
|
|
|
@ -19,7 +19,6 @@ mod digest;
|
|||
|
||||
use self::digest::Digest;
|
||||
|
||||
use blake2_rfc::blake2b::Blake2b;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use hex::ToHex;
|
||||
use md5::Context as Md5;
|
||||
|
@ -85,9 +84,13 @@ fn detect_algo<'a>(
|
|||
"sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256),
|
||||
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384),
|
||||
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
|
||||
"b2sum" => ("BLAKE2", Box::new(Blake2b::new(64)) as Box<dyn Digest>, 512),
|
||||
"b2sum" => (
|
||||
"BLAKE2",
|
||||
Box::new(blake2b_simd::State::new()) as Box<dyn Digest>,
|
||||
512,
|
||||
),
|
||||
"sha3sum" => match matches.value_of("bits") {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Some(bits_str) => match (bits_str).parse::<usize>() {
|
||||
Ok(224) => (
|
||||
"SHA3-224",
|
||||
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
||||
|
@ -137,7 +140,7 @@ fn detect_algo<'a>(
|
|||
512,
|
||||
),
|
||||
"shake128sum" => match matches.value_of("bits") {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Some(bits_str) => match (bits_str).parse::<usize>() {
|
||||
Ok(bits) => (
|
||||
"SHAKE128",
|
||||
Box::new(Shake128::new()) as Box<dyn Digest>,
|
||||
|
@ -148,7 +151,7 @@ fn detect_algo<'a>(
|
|||
None => crash!(1, "--bits required for SHAKE-128"),
|
||||
},
|
||||
"shake256sum" => match matches.value_of("bits") {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Some(bits_str) => match (bits_str).parse::<usize>() {
|
||||
Ok(bits) => (
|
||||
"SHAKE256",
|
||||
Box::new(Shake256::new()) as Box<dyn Digest>,
|
||||
|
@ -187,11 +190,11 @@ fn detect_algo<'a>(
|
|||
set_or_crash("SHA512", Box::new(Sha512::new()), 512)
|
||||
}
|
||||
if matches.is_present("b2sum") {
|
||||
set_or_crash("BLAKE2", Box::new(Blake2b::new(64)), 512)
|
||||
set_or_crash("BLAKE2", Box::new(blake2b_simd::State::new()), 512)
|
||||
}
|
||||
if matches.is_present("sha3") {
|
||||
match matches.value_of("bits") {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Some(bits_str) => match (bits_str).parse::<usize>() {
|
||||
Ok(224) => set_or_crash(
|
||||
"SHA3-224",
|
||||
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
||||
|
@ -235,7 +238,7 @@ fn detect_algo<'a>(
|
|||
}
|
||||
if matches.is_present("shake128") {
|
||||
match matches.value_of("bits") {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Some(bits_str) => match (bits_str).parse::<usize>() {
|
||||
Ok(bits) => set_or_crash("SHAKE128", Box::new(Shake128::new()), bits),
|
||||
Err(err) => crash!(1, "{}", err),
|
||||
},
|
||||
|
@ -244,7 +247,7 @@ fn detect_algo<'a>(
|
|||
}
|
||||
if matches.is_present("shake256") {
|
||||
match matches.value_of("bits") {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Some(bits_str) => match (bits_str).parse::<usize>() {
|
||||
Ok(bits) => set_or_crash("SHAKE256", Box::new(Shake256::new()), bits),
|
||||
Err(err) => crash!(1, "{}", err),
|
||||
},
|
||||
|
@ -252,10 +255,8 @@ fn detect_algo<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
if alg.is_none() {
|
||||
crash!(1, "You must specify hash algorithm!")
|
||||
};
|
||||
(name, alg.unwrap(), output_bits)
|
||||
let alg = alg.unwrap_or_else(|| crash!(1, "You must specify hash algorithm!"));
|
||||
(name, alg, output_bits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
// http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/i386/1.0-RELEASE/ports/shellutils/src/id.c
|
||||
// http://www.opensource.apple.com/source/shell_cmds/shell_cmds-118/id/id.c
|
||||
|
||||
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag
|
||||
// spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(dead_code)]
|
||||
|
@ -79,7 +79,7 @@ static OPT_GROUPS: &str = "groups";
|
|||
static OPT_HUMAN_READABLE: &str = "human-readable";
|
||||
static OPT_NAME: &str = "name";
|
||||
static OPT_PASSWORD: &str = "password";
|
||||
static OPT_REAL_ID: &str = "real-id";
|
||||
static OPT_REAL_ID: &str = "real";
|
||||
|
||||
static ARG_USERS: &str = "users";
|
||||
|
||||
|
@ -135,7 +135,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.arg(
|
||||
Arg::with_name(OPT_REAL_ID)
|
||||
.short("r")
|
||||
.help("Display the real ID for the -g and -u options"),
|
||||
.long(OPT_REAL_ID)
|
||||
.help(
|
||||
"Display the real ID for the -G, -g and -u options instead of the effective ID.",
|
||||
),
|
||||
)
|
||||
.arg(Arg::with_name(ARG_USERS).multiple(true).takes_value(true))
|
||||
.get_matches_from(args);
|
||||
|
@ -162,6 +165,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let nflag = matches.is_present(OPT_NAME);
|
||||
let uflag = matches.is_present(OPT_EFFECTIVE_USER);
|
||||
let gflag = matches.is_present(OPT_GROUP);
|
||||
let gsflag = matches.is_present(OPT_GROUPS);
|
||||
let rflag = matches.is_present(OPT_REAL_ID);
|
||||
|
||||
if gflag {
|
||||
|
@ -194,26 +198,23 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if matches.is_present(OPT_GROUPS) {
|
||||
if gsflag {
|
||||
let id = possible_pw
|
||||
.map(|p| p.gid())
|
||||
.unwrap_or(if rflag { getgid() } else { getegid() });
|
||||
println!(
|
||||
"{}",
|
||||
if nflag {
|
||||
possible_pw
|
||||
.map(|p| p.belongs_to())
|
||||
.unwrap_or_else(|| entries::get_groups().unwrap())
|
||||
.iter()
|
||||
.map(|&id| entries::gid2grp(id).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
} else {
|
||||
possible_pw
|
||||
.map(|p| p.belongs_to())
|
||||
.unwrap_or_else(|| entries::get_groups().unwrap())
|
||||
.iter()
|
||||
.map(|&id| id.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
possible_pw
|
||||
.map(|p| p.belongs_to())
|
||||
.unwrap_or_else(|| entries::get_groups_gnu(Some(id)).unwrap())
|
||||
.iter()
|
||||
.map(|&id| if nflag {
|
||||
entries::gid2grp(id).unwrap_or_else(|_| id.to_string())
|
||||
} else {
|
||||
id.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
@ -280,7 +281,7 @@ fn pretty(possible_pw: Option<Passwd>) {
|
|||
|
||||
println!(
|
||||
"groups\t{}",
|
||||
entries::get_groups()
|
||||
entries::get_groups_gnu(None)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|&gr| entries::gid2grp(gr).unwrap())
|
||||
|
|
|
@ -379,7 +379,7 @@ fn directory(paths: Vec<String>, b: Behavior) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
if mode::chmod(&path, b.mode()).is_err() {
|
||||
if mode::chmod(path, b.mode()).is_err() {
|
||||
all_successful = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -422,7 +422,7 @@ fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
|||
return 1;
|
||||
}
|
||||
|
||||
if mode::chmod(&parent, b.mode()).is_err() {
|
||||
if mode::chmod(parent, b.mode()).is_err() {
|
||||
show_error!("failed to chmod {}", parent.display());
|
||||
return 1;
|
||||
}
|
||||
|
@ -501,7 +501,7 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i3
|
|||
/// _target_ must be a non-directory
|
||||
///
|
||||
fn copy_file_to_file(file: &Path, target: &Path, b: &Behavior) -> i32 {
|
||||
if copy(file, &target, b).is_err() {
|
||||
if copy(file, target, b).is_err() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
|
@ -563,7 +563,7 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
|
|||
}
|
||||
}
|
||||
|
||||
if mode::chmod(&to, b.mode()).is_err() {
|
||||
if mode::chmod(to, b.mode()).is_err() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
|
|
|
@ -328,8 +328,8 @@ impl<'a> State<'a> {
|
|||
});
|
||||
} else {
|
||||
repr.print_field(key);
|
||||
repr.print_fields(&line1, self.key, self.max_fields);
|
||||
repr.print_fields(&line2, other.key, other.max_fields);
|
||||
repr.print_fields(line1, self.key, self.max_fields);
|
||||
repr.print_fields(line2, other.key, other.max_fields);
|
||||
}
|
||||
|
||||
println!();
|
||||
|
@ -611,7 +611,7 @@ fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
|
|||
|
||||
let mut state1 = State::new(
|
||||
FileNum::File1,
|
||||
&file1,
|
||||
file1,
|
||||
&stdin,
|
||||
settings.key1,
|
||||
settings.print_unpaired,
|
||||
|
@ -619,7 +619,7 @@ fn exec(file1: &str, file2: &str, settings: &Settings) -> i32 {
|
|||
|
||||
let mut state2 = State::new(
|
||||
FileNum::File2,
|
||||
&file2,
|
||||
file2,
|
||||
&stdin,
|
||||
settings.key2,
|
||||
settings.print_unpaired,
|
||||
|
|
|
@ -111,7 +111,7 @@ fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
|
|||
while i < args.len() {
|
||||
// this is safe because slice is valid when it is referenced
|
||||
let slice = &args[i].clone();
|
||||
if slice.starts_with('-') && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
|
||||
if slice.starts_with('-') && slice.chars().nth(1).map_or(false, |c| c.is_digit(10)) {
|
||||
let val = &slice[1..];
|
||||
match val.parse() {
|
||||
Ok(num) => {
|
||||
|
|
|
@ -259,17 +259,17 @@ fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
|
|||
// Handle cases where we create links in a directory first.
|
||||
if let Some(ref name) = settings.target_dir {
|
||||
// 4th form: a directory is specified by -t.
|
||||
return link_files_in_dir(files, &PathBuf::from(name), &settings);
|
||||
return link_files_in_dir(files, &PathBuf::from(name), settings);
|
||||
}
|
||||
if !settings.no_target_dir {
|
||||
if files.len() == 1 {
|
||||
// 2nd form: the target directory is the current directory.
|
||||
return link_files_in_dir(files, &PathBuf::from("."), &settings);
|
||||
return link_files_in_dir(files, &PathBuf::from("."), settings);
|
||||
}
|
||||
let last_file = &PathBuf::from(files.last().unwrap());
|
||||
if files.len() > 2 || last_file.is_dir() {
|
||||
// 3rd form: create links in the last argument.
|
||||
return link_files_in_dir(&files[0..files.len() - 1], last_file, &settings);
|
||||
return link_files_in_dir(&files[0..files.len() - 1], last_file, settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,7 +393,7 @@ fn relative_path<'a>(src: &Path, dst: &Path) -> Result<Cow<'a, Path>> {
|
|||
fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> {
|
||||
let mut backup_path = None;
|
||||
let source: Cow<'_, Path> = if settings.relative {
|
||||
relative_path(&src, dst)?
|
||||
relative_path(src, dst)?
|
||||
} else {
|
||||
src.into()
|
||||
};
|
||||
|
|
|
@ -1243,7 +1243,7 @@ fn sort_entries(entries: &mut Vec<PathData>, config: &Config) {
|
|||
Sort::Time => entries.sort_by_key(|k| {
|
||||
Reverse(
|
||||
k.md()
|
||||
.and_then(|md| get_system_time(&md, config))
|
||||
.and_then(|md| get_system_time(md, config))
|
||||
.unwrap_or(UNIX_EPOCH),
|
||||
)
|
||||
}),
|
||||
|
@ -1323,7 +1323,7 @@ fn enter_directory(dir: &PathData, config: &Config, out: &mut BufWriter<Stdout>)
|
|||
.filter(|p| p.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
|
||||
{
|
||||
let _ = writeln!(out, "\n{}:", e.p_buf.display());
|
||||
enter_directory(&e, config, out);
|
||||
enter_directory(e, config, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1339,8 +1339,8 @@ fn get_metadata(entry: &Path, dereference: bool) -> std::io::Result<Metadata> {
|
|||
fn display_dir_entry_size(entry: &PathData, config: &Config) -> (usize, usize) {
|
||||
if let Some(md) = entry.md() {
|
||||
(
|
||||
display_symlink_count(&md).len(),
|
||||
display_size_or_rdev(&md, config).len(),
|
||||
display_symlink_count(md).len(),
|
||||
display_size_or_rdev(md, config).len(),
|
||||
)
|
||||
} else {
|
||||
(0, 0)
|
||||
|
@ -1371,7 +1371,7 @@ fn display_items(items: &[PathData], config: &Config, out: &mut BufWriter<Stdout
|
|||
display_item_long(item, max_links, max_width, config, out);
|
||||
}
|
||||
} else {
|
||||
let names = items.iter().filter_map(|i| display_file_name(&i, config));
|
||||
let names = items.iter().filter_map(|i| display_file_name(i, config));
|
||||
|
||||
match (&config.format, config.width) {
|
||||
(Format::Columns, Some(width)) => {
|
||||
|
@ -1482,40 +1482,40 @@ fn display_item_long(
|
|||
#[cfg(unix)]
|
||||
{
|
||||
if config.inode {
|
||||
let _ = write!(out, "{} ", get_inode(&md));
|
||||
let _ = write!(out, "{} ", get_inode(md));
|
||||
}
|
||||
}
|
||||
|
||||
let _ = write!(
|
||||
out,
|
||||
"{} {}",
|
||||
display_permissions(&md, true),
|
||||
pad_left(display_symlink_count(&md), max_links),
|
||||
display_permissions(md, true),
|
||||
pad_left(display_symlink_count(md), max_links),
|
||||
);
|
||||
|
||||
if config.long.owner {
|
||||
let _ = write!(out, " {}", display_uname(&md, config));
|
||||
let _ = write!(out, " {}", display_uname(md, config));
|
||||
}
|
||||
|
||||
if config.long.group {
|
||||
let _ = write!(out, " {}", display_group(&md, config));
|
||||
let _ = write!(out, " {}", display_group(md, config));
|
||||
}
|
||||
|
||||
// Author is only different from owner on GNU/Hurd, so we reuse
|
||||
// the owner, since GNU/Hurd is not currently supported by Rust.
|
||||
if config.long.author {
|
||||
let _ = write!(out, " {}", display_uname(&md, config));
|
||||
let _ = write!(out, " {}", display_uname(md, config));
|
||||
}
|
||||
|
||||
let _ = writeln!(
|
||||
out,
|
||||
" {} {} {}",
|
||||
pad_left(display_size_or_rdev(md, config), max_size),
|
||||
display_date(&md, config),
|
||||
display_date(md, config),
|
||||
// unwrap is fine because it fails when metadata is not available
|
||||
// but we already know that it is because it's checked at the
|
||||
// start of the function.
|
||||
display_file_name(&item, config).unwrap().contents,
|
||||
display_file_name(item, config).unwrap().contents,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1741,7 +1741,7 @@ fn display_file_name(path: &PathData, config: &Config) -> Option<Cell> {
|
|||
let mut width = name.width();
|
||||
|
||||
if let Some(ls_colors) = &config.color {
|
||||
name = color_name(&ls_colors, &path.p_buf, name, path.md()?);
|
||||
name = color_name(ls_colors, &path.p_buf, name, path.md()?);
|
||||
}
|
||||
|
||||
if config.indicator_style != IndicatorStyle::None {
|
||||
|
@ -1786,7 +1786,7 @@ fn display_file_name(path: &PathData, config: &Config) -> Option<Cell> {
|
|||
}
|
||||
|
||||
fn color_name(ls_colors: &LsColors, path: &Path, name: String, md: &Metadata) -> String {
|
||||
match ls_colors.style_for_path_with_metadata(path, Some(&md)) {
|
||||
match ls_colors.style_for_path_with_metadata(path, Some(md)) {
|
||||
Some(style) => style.to_ansi_term_style().paint(name).to_string(),
|
||||
None => name,
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let mode_match = matches.value_of(OPT_MODE);
|
||||
let mode: u16 = match mode_match {
|
||||
Some(m) => {
|
||||
let res: Option<u16> = u16::from_str_radix(&m, 8).ok();
|
||||
let res: Option<u16> = u16::from_str_radix(m, 8).ok();
|
||||
match res {
|
||||
Some(r) => r,
|
||||
_ => crash!(1, "no mode given"),
|
||||
|
|
|
@ -59,7 +59,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
let mode = match matches.value_of(options::MODE) {
|
||||
Some(m) => match usize::from_str_radix(&m, 8) {
|
||||
Some(m) => match usize::from_str_radix(m, 8) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
show_error!("invalid mode: {}", e);
|
||||
|
|
|
@ -165,9 +165,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
};
|
||||
|
||||
if dry_run {
|
||||
dry_exec(tmpdir, prefix, rand, &suffix)
|
||||
dry_exec(tmpdir, prefix, rand, suffix)
|
||||
} else {
|
||||
exec(tmpdir, prefix, rand, &suffix, make_dir, suppress_file_err)
|
||||
exec(tmpdir, prefix, rand, suffix, make_dir, suppress_file_err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ clap = "2.33"
|
|||
uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" }
|
||||
uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" }
|
||||
crossterm = ">=0.19"
|
||||
atty = "0.2.14"
|
||||
atty = "0.2"
|
||||
unicode-width = "0.1.7"
|
||||
unicode-segmentation = "1.7.1"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox_termios = "0.1"
|
||||
|
|
|
@ -29,6 +29,9 @@ use crossterm::{
|
|||
terminal,
|
||||
};
|
||||
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
pub mod options {
|
||||
pub const SILENT: &str = "silent";
|
||||
pub const LOGICAL: &str = "logical";
|
||||
|
@ -140,7 +143,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
if let Some(files) = matches.values_of(options::FILES) {
|
||||
let mut stdout = setup_term();
|
||||
let length = files.len();
|
||||
for (idx, file) in files.enumerate() {
|
||||
|
||||
let mut files_iter = files.peekable();
|
||||
while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) {
|
||||
let file = Path::new(file);
|
||||
if file.is_dir() {
|
||||
terminal::disable_raw_mode().unwrap();
|
||||
|
@ -157,15 +162,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
let mut reader = BufReader::new(File::open(file).unwrap());
|
||||
reader.read_to_string(&mut buff).unwrap();
|
||||
let is_last = idx + 1 == length;
|
||||
more(&buff, &mut stdout, is_last);
|
||||
more(&buff, &mut stdout, next_file.copied());
|
||||
buff.clear();
|
||||
}
|
||||
reset_term(&mut stdout);
|
||||
} else if atty::isnt(atty::Stream::Stdin) {
|
||||
stdin().read_to_string(&mut buff).unwrap();
|
||||
let mut stdout = setup_term();
|
||||
more(&buff, &mut stdout, true);
|
||||
more(&buff, &mut stdout, None);
|
||||
reset_term(&mut stdout);
|
||||
} else {
|
||||
show_usage_error!("bad usage");
|
||||
|
@ -200,7 +204,7 @@ fn reset_term(stdout: &mut std::io::Stdout) {
|
|||
#[inline(always)]
|
||||
fn reset_term(_: &mut usize) {}
|
||||
|
||||
fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) {
|
||||
fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
|
||||
let (cols, rows) = terminal::size().unwrap();
|
||||
let lines = break_buff(buff, usize::from(cols));
|
||||
let line_count: u16 = lines.len().try_into().unwrap();
|
||||
|
@ -214,8 +218,11 @@ fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) {
|
|||
&mut stdout,
|
||||
lines.clone(),
|
||||
line_count,
|
||||
next_file,
|
||||
);
|
||||
|
||||
let is_last = next_file.is_none();
|
||||
|
||||
// Specifies whether we have reached the end of the file and should
|
||||
// return on the next key press. However, we immediately return when
|
||||
// this is the last file.
|
||||
|
@ -267,6 +274,7 @@ fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) {
|
|||
&mut stdout,
|
||||
lines.clone(),
|
||||
line_count,
|
||||
next_file,
|
||||
);
|
||||
|
||||
if lines_left == 0 {
|
||||
|
@ -285,6 +293,7 @@ fn draw(
|
|||
mut stdout: &mut std::io::Stdout,
|
||||
lines: Vec<String>,
|
||||
lc: u16,
|
||||
next_file: Option<&str>,
|
||||
) {
|
||||
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
|
||||
let (up_mark, lower_mark) = calc_range(*upper_mark, rows, lc);
|
||||
|
@ -299,7 +308,7 @@ fn draw(
|
|||
.write_all(format!("\r{}\n", line).as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
make_prompt_and_flush(&mut stdout, lower_mark, lc);
|
||||
make_prompt_and_flush(&mut stdout, lower_mark, lc, next_file);
|
||||
*upper_mark = up_mark;
|
||||
}
|
||||
|
||||
|
@ -313,23 +322,30 @@ fn break_buff(buff: &str, cols: usize) -> Vec<String> {
|
|||
lines
|
||||
}
|
||||
|
||||
fn break_line(mut line: &str, cols: usize) -> Vec<String> {
|
||||
let breaks = (line.len() / cols).saturating_add(1);
|
||||
let mut lines = Vec::with_capacity(breaks);
|
||||
// TODO: Use unicode width instead of the length in bytes.
|
||||
if line.len() < cols {
|
||||
fn break_line(line: &str, cols: usize) -> Vec<String> {
|
||||
let width = UnicodeWidthStr::width(line);
|
||||
let mut lines = Vec::new();
|
||||
if width < cols {
|
||||
lines.push(line.to_string());
|
||||
return lines;
|
||||
}
|
||||
|
||||
for _ in 1..=breaks {
|
||||
let (line1, line2) = line.split_at(cols);
|
||||
lines.push(line1.to_string());
|
||||
if line2.len() < cols {
|
||||
lines.push(line2.to_string());
|
||||
break;
|
||||
let gr_idx = UnicodeSegmentation::grapheme_indices(line, true);
|
||||
let mut last_index = 0;
|
||||
let mut total_width = 0;
|
||||
for (index, grapheme) in gr_idx {
|
||||
let width = UnicodeWidthStr::width(grapheme);
|
||||
total_width += width;
|
||||
|
||||
if total_width > cols {
|
||||
lines.push(line[last_index..index].to_string());
|
||||
last_index = index;
|
||||
total_width = width;
|
||||
}
|
||||
line = line2;
|
||||
}
|
||||
|
||||
if last_index != line.len() {
|
||||
lines.push(line[last_index..].to_string());
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
@ -339,7 +355,7 @@ fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) {
|
|||
let mut lower_mark = upper_mark.saturating_add(rows);
|
||||
|
||||
if lower_mark >= line_count {
|
||||
upper_mark = line_count.saturating_sub(rows);
|
||||
upper_mark = line_count.saturating_sub(rows).saturating_add(1);
|
||||
lower_mark = line_count;
|
||||
} else {
|
||||
lower_mark = lower_mark.saturating_sub(1)
|
||||
|
@ -348,12 +364,20 @@ fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) {
|
|||
}
|
||||
|
||||
// Make a prompt similar to original more
|
||||
fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16) {
|
||||
fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16, next_file: Option<&str>) {
|
||||
let status = if lower_mark == lc {
|
||||
format!("Next file: {}", next_file.unwrap_or_default())
|
||||
} else {
|
||||
format!(
|
||||
"{}%",
|
||||
(lower_mark as f64 / lc as f64 * 100.0).round() as u16
|
||||
)
|
||||
};
|
||||
write!(
|
||||
stdout,
|
||||
"\r{}--More--({}%){}",
|
||||
"\r{}--More--({}){}",
|
||||
Attribute::Reverse,
|
||||
((lower_mark as f64 / lc as f64) * 100.0).round() as u16,
|
||||
status,
|
||||
Attribute::Reset
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -363,13 +387,14 @@ fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16) {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{break_line, calc_range};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
// It is good to test the above functions
|
||||
#[test]
|
||||
fn test_calc_range() {
|
||||
assert_eq!((0, 24), calc_range(0, 25, 100));
|
||||
assert_eq!((50, 74), calc_range(50, 25, 100));
|
||||
assert_eq!((75, 100), calc_range(85, 25, 100));
|
||||
assert_eq!((76, 100), calc_range(85, 25, 100));
|
||||
}
|
||||
#[test]
|
||||
fn test_break_lines_long() {
|
||||
|
@ -379,11 +404,12 @@ mod tests {
|
|||
}
|
||||
|
||||
let lines = break_line(&test_string, 80);
|
||||
let widths: Vec<usize> = lines
|
||||
.iter()
|
||||
.map(|s| UnicodeWidthStr::width(&s[..]))
|
||||
.collect();
|
||||
|
||||
assert_eq!(
|
||||
(80, 80, 40),
|
||||
(lines[0].len(), lines[1].len(), lines[2].len())
|
||||
);
|
||||
assert_eq!((80, 80, 40), (widths[0], widths[1], widths[2]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -397,4 +423,22 @@ mod tests {
|
|||
|
||||
assert_eq!(20, lines[0].len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_break_line_zwj() {
|
||||
let mut test_string = String::with_capacity(1100);
|
||||
for _ in 0..20 {
|
||||
test_string.push_str("👩🏻🔬");
|
||||
}
|
||||
|
||||
let lines = break_line(&test_string, 80);
|
||||
|
||||
let widths: Vec<usize> = lines
|
||||
.iter()
|
||||
.map(|s| UnicodeWidthStr::width(&s[..]))
|
||||
.collect();
|
||||
|
||||
// Each 👩🏻🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
|
||||
assert_eq!((78, 42), (widths[0], widths[1]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -389,7 +389,7 @@ fn rename_with_fallback(from: &Path, to: &Path) -> io::Result<()> {
|
|||
let file_type = metadata.file_type();
|
||||
|
||||
if file_type.is_symlink() {
|
||||
rename_symlink_fallback(&from, &to)?;
|
||||
rename_symlink_fallback(from, to)?;
|
||||
} else if file_type.is_dir() {
|
||||
// We remove the destination directory if it exists to match the
|
||||
// behavior of `fs::rename`. As far as I can tell, `fs_extra`'s
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/nohup.rs"
|
|||
[dependencies]
|
||||
clap = "2.33"
|
||||
libc = "0.2.42"
|
||||
atty = "0.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ use std::fs::{File, OpenOptions};
|
|||
use std::io::Error;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::fs::{is_stderr_interactive, is_stdin_interactive, is_stdout_interactive};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static ABOUT: &str = "Run COMMAND ignoring hangup signals.";
|
||||
|
@ -84,7 +83,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
fn replace_fds() {
|
||||
if is_stdin_interactive() {
|
||||
if atty::is(atty::Stream::Stdin) {
|
||||
let new_stdin = match File::open(Path::new("/dev/null")) {
|
||||
Ok(t) => t,
|
||||
Err(e) => crash!(2, "Cannot replace STDIN: {}", e),
|
||||
|
@ -94,7 +93,7 @@ fn replace_fds() {
|
|||
}
|
||||
}
|
||||
|
||||
if is_stdout_interactive() {
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
let new_stdout = find_stdout();
|
||||
let fd = new_stdout.as_raw_fd();
|
||||
|
||||
|
@ -103,7 +102,7 @@ fn replace_fds() {
|
|||
}
|
||||
}
|
||||
|
||||
if is_stderr_interactive() && unsafe { dup2(1, 2) } != 2 {
|
||||
if atty::is(atty::Stream::Stderr) && unsafe { dup2(1, 2) } != 2 {
|
||||
crash!(2, "Cannot replace STDERR: {}", Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,7 +238,7 @@ fn format_and_print_delimited(s: &str, options: &NumfmtOptions) -> Result<()> {
|
|||
}
|
||||
|
||||
if field_selected {
|
||||
print!("{}", format_string(&field.trim_start(), options, None)?);
|
||||
print!("{}", format_string(field.trim_start(), options, None)?);
|
||||
} else {
|
||||
// print unselected field without conversion
|
||||
print!("{}", field);
|
||||
|
@ -271,7 +271,7 @@ fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> {
|
|||
None
|
||||
};
|
||||
|
||||
print!("{}", format_string(&field, options, implicit_padding)?);
|
||||
print!("{}", format_string(field, options, implicit_padding)?);
|
||||
} else {
|
||||
// print unselected field without conversion
|
||||
print!("{}{}", prefix, field);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Copy)]
|
||||
pub enum FormatWriter {
|
||||
IntWriter(fn(u64) -> String),
|
||||
|
|
|
@ -115,7 +115,7 @@ impl<'a> MemoryDecoder<'a> {
|
|||
|
||||
/// Creates a clone of the internal buffer. The clone only contain the valid data.
|
||||
pub fn clone_buffer(&self, other: &mut Vec<u8>) {
|
||||
other.clone_from(&self.data);
|
||||
other.clone_from(self.data);
|
||||
other.resize(self.used_normal_length, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ impl OdOptions {
|
|||
|
||||
let mut skip_bytes = match matches.value_of(options::SKIP_BYTES) {
|
||||
None => 0,
|
||||
Some(s) => match parse_number_of_bytes(&s) {
|
||||
Some(s) => match parse_number_of_bytes(s) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return Err(format!("Invalid argument --skip-bytes={}", s));
|
||||
|
@ -176,7 +176,7 @@ impl OdOptions {
|
|||
|
||||
let read_bytes = match matches.value_of(options::READ_BYTES) {
|
||||
None => None,
|
||||
Some(s) => match parse_number_of_bytes(&s) {
|
||||
Some(s) => match parse_number_of_bytes(s) {
|
||||
Ok(i) => Some(i),
|
||||
Err(_) => {
|
||||
return Err(format!("Invalid argument --read-bytes={}", s));
|
||||
|
@ -537,7 +537,7 @@ where
|
|||
print_bytes(
|
||||
&input_offset.format_byte_offset(),
|
||||
&memory_decoder,
|
||||
&output_info,
|
||||
output_info,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ impl OutputInfo {
|
|||
let print_width_line = print_width_block * (line_bytes / byte_size_block);
|
||||
|
||||
let spaced_formatters =
|
||||
OutputInfo::create_spaced_formatter_info(&formats, byte_size_block, print_width_block);
|
||||
OutputInfo::create_spaced_formatter_info(formats, byte_size_block, print_width_block);
|
||||
|
||||
OutputInfo {
|
||||
byte_size_line: line_bytes,
|
||||
|
|
|
@ -275,17 +275,13 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
|
|||
let mut chars = params.chars();
|
||||
let mut ch = chars.next();
|
||||
|
||||
while ch.is_some() {
|
||||
let type_char = ch.unwrap();
|
||||
let type_char = match format_type(type_char) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
return Err(format!(
|
||||
"unexpected char '{}' in format specification '{}'",
|
||||
type_char, params
|
||||
));
|
||||
}
|
||||
};
|
||||
while let Some(type_char) = ch {
|
||||
let type_char = format_type(type_char).ok_or_else(|| {
|
||||
format!(
|
||||
"unexpected char '{}' in format specification '{}'",
|
||||
type_char, params
|
||||
)
|
||||
})?;
|
||||
|
||||
let type_cat = format_type_category(type_char);
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs,
|
|||
// if any of the options -A, -j, -N, -t, -v or -w are present there is no offset
|
||||
if !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) {
|
||||
// test if the last input can be parsed as an offset.
|
||||
let offset = parse_offset_operand(&input_strings[input_strings.len() - 1]);
|
||||
let offset = parse_offset_operand(input_strings[input_strings.len() - 1]);
|
||||
if let Ok(n) = offset {
|
||||
// if there is just 1 input (stdin), an offset must start with '+'
|
||||
if input_strings.len() == 1 && input_strings[0].starts_with('+') {
|
||||
|
@ -88,7 +88,7 @@ pub fn parse_inputs_traditional(input_strings: Vec<&str>) -> Result<CommandLineI
|
|||
match input_strings.len() {
|
||||
0 => Ok(CommandLineInputs::FileNames(vec!["-".to_string()])),
|
||||
1 => {
|
||||
let offset0 = parse_offset_operand(&input_strings[0]);
|
||||
let offset0 = parse_offset_operand(input_strings[0]);
|
||||
Ok(match offset0 {
|
||||
Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)),
|
||||
_ => CommandLineInputs::FileNames(
|
||||
|
@ -97,8 +97,8 @@ pub fn parse_inputs_traditional(input_strings: Vec<&str>) -> Result<CommandLineI
|
|||
})
|
||||
}
|
||||
2 => {
|
||||
let offset0 = parse_offset_operand(&input_strings[0]);
|
||||
let offset1 = parse_offset_operand(&input_strings[1]);
|
||||
let offset0 = parse_offset_operand(input_strings[0]);
|
||||
let offset1 = parse_offset_operand(input_strings[1]);
|
||||
match (offset0, offset1) {
|
||||
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
||||
"-".to_string(),
|
||||
|
@ -114,8 +114,8 @@ pub fn parse_inputs_traditional(input_strings: Vec<&str>) -> Result<CommandLineI
|
|||
}
|
||||
}
|
||||
3 => {
|
||||
let offset = parse_offset_operand(&input_strings[1]);
|
||||
let label = parse_offset_operand(&input_strings[2]);
|
||||
let offset = parse_offset_operand(input_strings[1]);
|
||||
let label = parse_offset_operand(input_strings[2]);
|
||||
match (offset, label) {
|
||||
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
||||
input_strings[0].to_string(),
|
||||
|
|
|
@ -118,10 +118,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
// check a path, given as a slice of it's components and an operating mode
|
||||
fn check_path(mode: &Mode, path: &[String]) -> bool {
|
||||
match *mode {
|
||||
Mode::Basic => check_basic(&path),
|
||||
Mode::Extra => check_default(&path) && check_extra(&path),
|
||||
Mode::Both => check_basic(&path) && check_extra(&path),
|
||||
_ => check_default(&path),
|
||||
Mode::Basic => check_basic(path),
|
||||
Mode::Extra => check_default(path) && check_extra(path),
|
||||
Mode::Both => check_basic(path) && check_extra(path),
|
||||
_ => check_default(path),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ fn check_basic(path: &[String]) -> bool {
|
|||
);
|
||||
return false;
|
||||
}
|
||||
if !check_portable_chars(&p) {
|
||||
if !check_portable_chars(p) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ fn check_basic(path: &[String]) -> bool {
|
|||
fn check_extra(path: &[String]) -> bool {
|
||||
// components: leading hyphens
|
||||
for p in path {
|
||||
if !no_leading_hyphen(&p) {
|
||||
if !no_leading_hyphen(p) {
|
||||
writeln!(
|
||||
&mut std::io::stderr(),
|
||||
"leading hyphen in file name component '{}'",
|
||||
|
|
|
@ -283,7 +283,7 @@ impl Pinky {
|
|||
}
|
||||
}
|
||||
|
||||
print!(" {}", time_string(&ut));
|
||||
print!(" {}", time_string(ut));
|
||||
|
||||
let mut s = ut.host();
|
||||
if self.include_where && !s.is_empty() {
|
||||
|
|
|
@ -410,7 +410,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let options = &result_options.unwrap();
|
||||
|
||||
let cmd_result = if file_group.len() == 1 {
|
||||
pr(&file_group.get(0).unwrap(), options)
|
||||
pr(file_group.get(0).unwrap(), options)
|
||||
} else {
|
||||
mpr(&file_group, options)
|
||||
};
|
||||
|
@ -1114,7 +1114,7 @@ fn write_columns(
|
|||
for (i, cell) in row.iter().enumerate() {
|
||||
if cell.is_none() && options.merge_files_print.is_some() {
|
||||
out.write_all(
|
||||
get_line_for_printing(&options, &blank_line, columns, i, &line_width, indexes)
|
||||
get_line_for_printing(options, &blank_line, columns, i, &line_width, indexes)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
} else if cell.is_none() {
|
||||
|
@ -1124,7 +1124,7 @@ fn write_columns(
|
|||
let file_line = cell.unwrap();
|
||||
|
||||
out.write_all(
|
||||
get_line_for_printing(&options, file_line, columns, i, &line_width, indexes)
|
||||
get_line_for_printing(options, file_line, columns, i, &line_width, indexes)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
lines_printed += 1;
|
||||
|
@ -1149,7 +1149,7 @@ fn get_line_for_printing(
|
|||
indexes: usize,
|
||||
) -> String {
|
||||
let blank_line = String::new();
|
||||
let formatted_line_number = get_formatted_line_number(&options, file_line.line_number, index);
|
||||
let formatted_line_number = get_formatted_line_number(options, file_line.line_number, index);
|
||||
|
||||
let mut complete_line = format!(
|
||||
"{}{}",
|
||||
|
|
|
@ -26,7 +26,7 @@ impl Formatter for CninetyNineHexFloatf {
|
|||
) -> Option<FormatPrimitive> {
|
||||
let second_field = field.second_field.unwrap_or(6) + 1;
|
||||
let analysis = FloatAnalysis::analyze(
|
||||
&str_in,
|
||||
str_in,
|
||||
initial_prefix,
|
||||
Some(second_field as usize),
|
||||
None,
|
||||
|
|
|
@ -298,11 +298,11 @@ pub fn get_primitive_dec(
|
|||
pub fn primitive_to_str_common(prim: &FormatPrimitive, field: &FormatField) -> String {
|
||||
let mut final_str = String::new();
|
||||
if let Some(ref prefix) = prim.prefix {
|
||||
final_str.push_str(&prefix);
|
||||
final_str.push_str(prefix);
|
||||
}
|
||||
match prim.pre_decimal {
|
||||
Some(ref pre_decimal) => {
|
||||
final_str.push_str(&pre_decimal);
|
||||
final_str.push_str(pre_decimal);
|
||||
}
|
||||
None => {
|
||||
panic!(
|
||||
|
|
|
@ -21,7 +21,7 @@ impl Formatter for Floatf {
|
|||
) -> Option<FormatPrimitive> {
|
||||
let second_field = field.second_field.unwrap_or(6) + 1;
|
||||
let analysis = FloatAnalysis::analyze(
|
||||
&str_in,
|
||||
str_in,
|
||||
initial_prefix,
|
||||
None,
|
||||
Some(second_field as usize),
|
||||
|
|
|
@ -252,7 +252,7 @@ impl Formatter for Intf {
|
|||
fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String {
|
||||
let mut final_str: String = String::new();
|
||||
if let Some(ref prefix) = prim.prefix {
|
||||
final_str.push_str(&prefix);
|
||||
final_str.push_str(prefix);
|
||||
}
|
||||
// integral second fields is zero-padded minimum-width
|
||||
// which gets handled before general minimum-width
|
||||
|
@ -266,7 +266,7 @@ impl Formatter for Intf {
|
|||
i -= 1;
|
||||
}
|
||||
}
|
||||
final_str.push_str(&pre_decimal);
|
||||
final_str.push_str(pre_decimal);
|
||||
}
|
||||
None => {
|
||||
panic!(
|
||||
|
|
|
@ -235,7 +235,7 @@ pub fn num_format(field: &FormatField, in_str_opt: Option<&String>) -> Option<St
|
|||
let as_str = format!("{}", provided_num);
|
||||
let initial_prefix = get_initial_prefix(
|
||||
&as_str,
|
||||
&field.field_type
|
||||
field.field_type
|
||||
);
|
||||
tmp=formatter.get_primitive(field, &initial_prefix, &as_str)
|
||||
.expect("err during default provided num");
|
||||
|
@ -258,7 +258,7 @@ pub fn num_format(field: &FormatField, in_str_opt: Option<&String>) -> Option<St
|
|||
// any formatter (int or float)
|
||||
let initial_prefix = get_initial_prefix(
|
||||
in_str,
|
||||
&field.field_type
|
||||
field.field_type
|
||||
);
|
||||
// then get the FormatPrimitive from the Formatter
|
||||
formatter.get_primitive(field, &initial_prefix, in_str)
|
||||
|
|
|
@ -275,7 +275,7 @@ impl SubParser {
|
|||
}
|
||||
None => {
|
||||
text_so_far.push('%');
|
||||
err_conv(&text_so_far);
|
||||
err_conv(text_so_far);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ fn read_input(input_files: &[String], config: &Config) -> FileMap {
|
|||
files.push("-");
|
||||
} else if config.gnu_ext {
|
||||
for file in input_files {
|
||||
files.push(&file);
|
||||
files.push(file);
|
||||
}
|
||||
} else {
|
||||
files.push(&input_files[0]);
|
||||
|
@ -503,7 +503,7 @@ fn format_tex_line(
|
|||
let keyword = &line[word_ref.position..word_ref.position_end];
|
||||
let after_chars_trim_idx = (word_ref.position_end, chars_line.len());
|
||||
let all_after = &chars_line[after_chars_trim_idx.0..after_chars_trim_idx.1];
|
||||
let (tail, before, after, head) = get_output_chunks(&all_before, &keyword, &all_after, &config);
|
||||
let (tail, before, after, head) = get_output_chunks(all_before, keyword, all_after, config);
|
||||
output.push_str(&format!(
|
||||
"{5}{0}{6}{5}{1}{6}{5}{2}{6}{5}{3}{6}{5}{4}{6}",
|
||||
format_tex_field(&tail),
|
||||
|
@ -515,7 +515,7 @@ fn format_tex_line(
|
|||
"}"
|
||||
));
|
||||
if config.auto_ref || config.input_ref {
|
||||
output.push_str(&format!("{}{}{}", "{", format_tex_field(&reference), "}"));
|
||||
output.push_str(&format!("{}{}{}", "{", format_tex_field(reference), "}"));
|
||||
}
|
||||
output
|
||||
}
|
||||
|
@ -546,7 +546,7 @@ fn format_roff_line(
|
|||
let keyword = &line[word_ref.position..word_ref.position_end];
|
||||
let after_chars_trim_idx = (word_ref.position_end, chars_line.len());
|
||||
let all_after = &chars_line[after_chars_trim_idx.0..after_chars_trim_idx.1];
|
||||
let (tail, before, after, head) = get_output_chunks(&all_before, &keyword, &all_after, &config);
|
||||
let (tail, before, after, head) = get_output_chunks(all_before, keyword, all_after, config);
|
||||
output.push_str(&format!(
|
||||
" \"{}\" \"{}\" \"{}{}\" \"{}\"",
|
||||
format_roff_field(&tail),
|
||||
|
@ -556,7 +556,7 @@ fn format_roff_line(
|
|||
format_roff_field(&head)
|
||||
));
|
||||
if config.auto_ref || config.input_ref {
|
||||
output.push_str(&format!(" \"{}\"", format_roff_field(&reference)));
|
||||
output.push_str(&format!(" \"{}\"", format_roff_field(reference)));
|
||||
}
|
||||
output
|
||||
}
|
||||
|
|
|
@ -18,10 +18,12 @@ path = "src/rm.rs"
|
|||
clap = "2.33"
|
||||
walkdir = "2.2"
|
||||
remove_dir_all = "0.5.1"
|
||||
winapi = { version="0.3", features=[] }
|
||||
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "rm"
|
||||
path = "src/main.rs"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) bitor ulong
|
||||
// spell-checker:ignore (path) eacces
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
@ -430,9 +430,7 @@ use std::os::windows::prelude::MetadataExt;
|
|||
|
||||
#[cfg(windows)]
|
||||
fn is_symlink_dir(metadata: &fs::Metadata) -> bool {
|
||||
use std::os::raw::c_ulong;
|
||||
pub type DWORD = c_ulong;
|
||||
pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;
|
||||
use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
metadata.file_type().is_symlink()
|
||||
&& ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
|
|
|
@ -88,7 +88,7 @@ fn remove(dirs: Vec<String>, ignore: bool, parents: bool, verbose: bool) -> Resu
|
|||
|
||||
for dir in &dirs {
|
||||
let path = Path::new(&dir[..]);
|
||||
r = remove_dir(&path, ignore, verbose).and(r);
|
||||
r = remove_dir(path, ignore, verbose).and(r);
|
||||
if parents {
|
||||
let mut p = path;
|
||||
while let Some(new_p) = p.parent() {
|
||||
|
|
|
@ -381,7 +381,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
for path_str in matches.values_of(options::FILE).unwrap() {
|
||||
wipe_file(
|
||||
&path_str, iterations, remove, size, exact, zero, verbose, force,
|
||||
path_str, iterations, remove, size, exact, zero, verbose, force,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -659,7 +659,7 @@ fn do_remove(path: &Path, orig_filename: &str, verbose: bool) -> Result<(), io::
|
|||
println!("{}: {}: removing", NAME, orig_filename);
|
||||
}
|
||||
|
||||
let renamed_path: Option<PathBuf> = wipe_name(&path, verbose);
|
||||
let renamed_path: Option<PathBuf> = wipe_name(path, verbose);
|
||||
if let Some(rp) = renamed_path {
|
||||
fs::remove_file(rp)?;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ pub fn check(path: &str, settings: &GlobalSettings) -> i32 {
|
|||
let prev_last = prev_chunk.borrow_lines().last().unwrap();
|
||||
let new_first = chunk.borrow_lines().first().unwrap();
|
||||
|
||||
if compare_by(prev_last, new_first, &settings) == Ordering::Greater {
|
||||
if compare_by(prev_last, new_first, settings) == Ordering::Greater {
|
||||
if !settings.check_silent {
|
||||
println!("sort: {}:{}: disorder: {}", path, line_idx, new_first.line);
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ pub fn check(path: &str, settings: &GlobalSettings) -> i32 {
|
|||
|
||||
for (a, b) in chunk.borrow_lines().iter().tuple_windows() {
|
||||
line_idx += 1;
|
||||
if compare_by(a, b, &settings) == Ordering::Greater {
|
||||
if compare_by(a, b, settings) == Ordering::Greater {
|
||||
if !settings.check_silent {
|
||||
println!("sort: {}:{}: disorder: {}", path, line_idx, b.line);
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ pub fn read(
|
|||
if buffer.len() < carry_over.len() {
|
||||
buffer.resize(carry_over.len() + 10 * 1024, 0);
|
||||
}
|
||||
buffer[..carry_over.len()].copy_from_slice(&carry_over);
|
||||
buffer[..carry_over.len()].copy_from_slice(carry_over);
|
||||
let (read, should_continue) = read_to_buffer(
|
||||
file,
|
||||
next_files,
|
||||
|
@ -102,17 +102,17 @@ pub fn read(
|
|||
carry_over.clear();
|
||||
carry_over.extend_from_slice(&buffer[read..]);
|
||||
|
||||
let payload = Chunk::new(buffer, |buf| {
|
||||
let mut lines = unsafe {
|
||||
// SAFETY: It is safe to transmute to a vector of lines with shorter lifetime,
|
||||
// because it was only temporarily transmuted to a Vec<Line<'static>> to make recycling possible.
|
||||
std::mem::transmute::<Vec<Line<'static>>, Vec<Line<'_>>>(lines)
|
||||
};
|
||||
let read = crash_if_err!(1, std::str::from_utf8(&buf[..read]));
|
||||
parse_lines(read, &mut lines, separator, &settings);
|
||||
lines
|
||||
});
|
||||
if !payload.borrow_lines().is_empty() {
|
||||
if read != 0 {
|
||||
let payload = Chunk::new(buffer, |buf| {
|
||||
let mut lines = unsafe {
|
||||
// SAFETY: It is safe to transmute to a vector of lines with shorter lifetime,
|
||||
// because it was only temporarily transmuted to a Vec<Line<'static>> to make recycling possible.
|
||||
std::mem::transmute::<Vec<Line<'static>>, Vec<Line<'_>>>(lines)
|
||||
};
|
||||
let read = crash_if_err!(1, std::str::from_utf8(&buf[..read]));
|
||||
parse_lines(read, &mut lines, separator, settings);
|
||||
lines
|
||||
});
|
||||
sender.send(payload).unwrap();
|
||||
}
|
||||
if !should_continue {
|
||||
|
@ -175,6 +175,7 @@ fn read_to_buffer(
|
|||
separator: u8,
|
||||
) -> (usize, bool) {
|
||||
let mut read_target = &mut buffer[start_offset..];
|
||||
let mut last_file_target_size = read_target.len();
|
||||
loop {
|
||||
match file.read(read_target) {
|
||||
Ok(0) => {
|
||||
|
@ -193,7 +194,7 @@ fn read_to_buffer(
|
|||
continue;
|
||||
}
|
||||
}
|
||||
let mut sep_iter = memchr_iter(separator, &buffer).rev();
|
||||
let mut sep_iter = memchr_iter(separator, buffer).rev();
|
||||
let last_line_end = sep_iter.next();
|
||||
if sep_iter.next().is_some() {
|
||||
// We read enough lines.
|
||||
|
@ -208,14 +209,27 @@ fn read_to_buffer(
|
|||
read_target = &mut buffer[len..];
|
||||
}
|
||||
} else {
|
||||
// This file is empty.
|
||||
// This file has been fully read.
|
||||
let mut leftover_len = read_target.len();
|
||||
if last_file_target_size != leftover_len {
|
||||
// The file was not empty.
|
||||
let read_len = buffer.len() - leftover_len;
|
||||
if buffer[read_len - 1] != separator {
|
||||
// The file did not end with a separator. We have to insert one.
|
||||
buffer[read_len] = separator;
|
||||
leftover_len -= 1;
|
||||
}
|
||||
let read_len = buffer.len() - leftover_len;
|
||||
read_target = &mut buffer[read_len..];
|
||||
}
|
||||
if let Some(next_file) = next_files.next() {
|
||||
// There is another file.
|
||||
last_file_target_size = leftover_len;
|
||||
*file = next_file;
|
||||
} else {
|
||||
// This was the last file.
|
||||
let leftover_len = read_target.len();
|
||||
return (buffer.len() - leftover_len, false);
|
||||
let read_len = buffer.len() - leftover_len;
|
||||
return (read_len, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ pub fn custom_str_cmp(
|
|||
) -> Ordering {
|
||||
if !(ignore_case || ignore_non_dictionary || ignore_non_printing) {
|
||||
// There are no custom settings. Fall back to the default strcmp, which is faster.
|
||||
return a.cmp(&b);
|
||||
return a.cmp(b);
|
||||
}
|
||||
let mut a_chars = a
|
||||
.chars()
|
||||
|
|
|
@ -12,8 +12,12 @@
|
|||
//! The buffers for the individual chunks are recycled. There are two buffers.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Child;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::Read,
|
||||
|
@ -25,12 +29,13 @@ use itertools::Itertools;
|
|||
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::Line;
|
||||
use crate::{
|
||||
chunks::{self, Chunk},
|
||||
compare_by, merge, output_sorted_lines, sort_by, GlobalSettings,
|
||||
};
|
||||
|
||||
const MIN_BUFFER_SIZE: usize = 8_000;
|
||||
const START_BUFFER_SIZE: usize = 8_000;
|
||||
|
||||
/// Sort files by using auxiliary files for storing intermediate chunks (if needed), and output the result.
|
||||
pub fn ext_sort(files: &mut impl Iterator<Item = Box<dyn Read + Send>>, settings: &GlobalSettings) {
|
||||
|
@ -63,10 +68,31 @@ pub fn ext_sort(files: &mut impl Iterator<Item = Box<dyn Read + Send>>, settings
|
|||
);
|
||||
match read_result {
|
||||
ReadResult::WroteChunksToFile { chunks_written } => {
|
||||
let files = (0..chunks_written)
|
||||
.map(|chunk_num| tmp_dir.path().join(chunk_num.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
let mut merger = merge::merge(&files, settings);
|
||||
let mut children = Vec::new();
|
||||
let files = (0..chunks_written).map(|chunk_num| {
|
||||
let file_path = tmp_dir.path().join(chunk_num.to_string());
|
||||
let file = File::open(file_path).unwrap();
|
||||
if let Some(compress_prog) = &settings.compress_prog {
|
||||
let mut command = Command::new(compress_prog);
|
||||
command.stdin(file).stdout(Stdio::piped()).arg("-d");
|
||||
let mut child = crash_if_err!(
|
||||
2,
|
||||
command.spawn().map_err(|err| format!(
|
||||
"couldn't execute compress program: errno {}",
|
||||
err.raw_os_error().unwrap()
|
||||
))
|
||||
);
|
||||
let child_stdout = child.stdout.take().unwrap();
|
||||
children.push(child);
|
||||
Box::new(BufReader::new(child_stdout)) as Box<dyn Read + Send>
|
||||
} else {
|
||||
Box::new(BufReader::new(file)) as Box<dyn Read + Send>
|
||||
}
|
||||
});
|
||||
let mut merger = merge::merge_with_file_limit(files, settings);
|
||||
for child in children {
|
||||
assert_child_success(child, settings.compress_prog.as_ref().unwrap());
|
||||
}
|
||||
merger.write_all(settings);
|
||||
}
|
||||
ReadResult::SortedSingleChunk(chunk) => {
|
||||
|
@ -132,7 +158,14 @@ fn reader_writer(
|
|||
for _ in 0..2 {
|
||||
chunks::read(
|
||||
&mut sender_option,
|
||||
vec![0; MIN_BUFFER_SIZE],
|
||||
vec![
|
||||
0;
|
||||
if START_BUFFER_SIZE < buffer_size {
|
||||
START_BUFFER_SIZE
|
||||
} else {
|
||||
buffer_size
|
||||
}
|
||||
],
|
||||
Some(buffer_size),
|
||||
&mut carry_over,
|
||||
&mut file,
|
||||
|
@ -171,6 +204,7 @@ fn reader_writer(
|
|||
write(
|
||||
&mut chunk,
|
||||
&tmp_dir.path().join(file_number.to_string()),
|
||||
settings.compress_prog.as_deref(),
|
||||
separator,
|
||||
);
|
||||
|
||||
|
@ -193,14 +227,45 @@ fn reader_writer(
|
|||
}
|
||||
|
||||
/// Write the lines in `chunk` to `file`, separated by `separator`.
|
||||
fn write(chunk: &mut Chunk, file: &Path, separator: u8) {
|
||||
/// `compress_prog` is used to optionally compress file contents.
|
||||
fn write(chunk: &mut Chunk, file: &Path, compress_prog: Option<&str>, separator: u8) {
|
||||
chunk.with_lines_mut(|lines| {
|
||||
// Write the lines to the file
|
||||
let file = crash_if_err!(1, OpenOptions::new().create(true).write(true).open(file));
|
||||
let mut writer = BufWriter::new(file);
|
||||
for s in lines.iter() {
|
||||
crash_if_err!(1, writer.write_all(s.line.as_bytes()));
|
||||
crash_if_err!(1, writer.write_all(&[separator]));
|
||||
}
|
||||
if let Some(compress_prog) = compress_prog {
|
||||
let mut command = Command::new(compress_prog);
|
||||
command.stdin(Stdio::piped()).stdout(file);
|
||||
let mut child = crash_if_err!(
|
||||
2,
|
||||
command.spawn().map_err(|err| format!(
|
||||
"couldn't execute compress program: errno {}",
|
||||
err.raw_os_error().unwrap()
|
||||
))
|
||||
);
|
||||
let mut writer = BufWriter::new(child.stdin.take().unwrap());
|
||||
write_lines(lines, &mut writer, separator);
|
||||
writer.flush().unwrap();
|
||||
drop(writer);
|
||||
assert_child_success(child, compress_prog);
|
||||
} else {
|
||||
let mut writer = BufWriter::new(file);
|
||||
write_lines(lines, &mut writer, separator);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn write_lines<'a, T: Write>(lines: &[Line<'a>], writer: &mut T, separator: u8) {
|
||||
for s in lines {
|
||||
crash_if_err!(1, writer.write_all(s.line.as_bytes()));
|
||||
crash_if_err!(1, writer.write_all(&[separator]));
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_child_success(mut child: Child, program: &str) {
|
||||
if !matches!(
|
||||
child.wait().map(|e| e.code()),
|
||||
Ok(Some(0)) | Ok(None) | Err(_)
|
||||
) {
|
||||
crash!(2, "'{}' terminated abnormally", program)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
ffi::OsStr,
|
||||
io::{Read, Write},
|
||||
fs::File,
|
||||
io::{BufWriter, Read, Write},
|
||||
iter,
|
||||
rc::Rc,
|
||||
sync::mpsc::{channel, sync_channel, Receiver, Sender, SyncSender},
|
||||
|
@ -18,18 +18,69 @@ use std::{
|
|||
};
|
||||
|
||||
use compare::Compare;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
chunks::{self, Chunk},
|
||||
compare_by, open, GlobalSettings,
|
||||
compare_by, GlobalSettings,
|
||||
};
|
||||
|
||||
// Merge already sorted files.
|
||||
pub fn merge<'a>(files: &[impl AsRef<OsStr>], settings: &'a GlobalSettings) -> FileMerger<'a> {
|
||||
pub fn merge_with_file_limit<F: ExactSizeIterator<Item = Box<dyn Read + Send>>>(
|
||||
files: F,
|
||||
settings: &GlobalSettings,
|
||||
) -> FileMerger {
|
||||
if files.len() > settings.merge_batch_size {
|
||||
let tmp_dir = tempfile::Builder::new()
|
||||
.prefix("uutils_sort")
|
||||
.tempdir_in(&settings.tmp_dir)
|
||||
.unwrap();
|
||||
let mut batch_number = 0;
|
||||
let mut remaining_files = files.len();
|
||||
let batches = files.chunks(settings.merge_batch_size);
|
||||
let mut batches = batches.into_iter();
|
||||
while batch_number + remaining_files > settings.merge_batch_size && remaining_files != 0 {
|
||||
remaining_files = remaining_files.saturating_sub(settings.merge_batch_size);
|
||||
let mut merger = merge_without_limit(batches.next().unwrap(), settings);
|
||||
let tmp_file = File::create(tmp_dir.path().join(batch_number.to_string())).unwrap();
|
||||
merger.write_all_to(settings, &mut BufWriter::new(tmp_file));
|
||||
batch_number += 1;
|
||||
}
|
||||
let batch_files = (0..batch_number).map(|n| {
|
||||
Box::new(File::open(tmp_dir.path().join(n.to_string())).unwrap())
|
||||
as Box<dyn Read + Send>
|
||||
});
|
||||
if batch_number > settings.merge_batch_size {
|
||||
assert!(batches.next().is_none());
|
||||
merge_with_file_limit(
|
||||
Box::new(batch_files) as Box<dyn ExactSizeIterator<Item = Box<dyn Read + Send>>>,
|
||||
settings,
|
||||
)
|
||||
} else {
|
||||
let final_batch = batches.next();
|
||||
assert!(batches.next().is_none());
|
||||
merge_without_limit(
|
||||
batch_files.chain(final_batch.into_iter().flatten()),
|
||||
settings,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
merge_without_limit(files, settings)
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge files without limiting how many files are concurrently open
|
||||
///
|
||||
/// It is the responsibility of the caller to ensure that `files` yields only
|
||||
/// as many files as we are allowed to open concurrently.
|
||||
fn merge_without_limit<F: Iterator<Item = Box<dyn Read + Send>>>(
|
||||
files: F,
|
||||
settings: &GlobalSettings,
|
||||
) -> FileMerger {
|
||||
let (request_sender, request_receiver) = channel();
|
||||
let mut reader_files = Vec::with_capacity(files.len());
|
||||
let mut loaded_receivers = Vec::with_capacity(files.len());
|
||||
for (file_number, file) in files.iter().map(open).enumerate() {
|
||||
let mut reader_files = Vec::with_capacity(files.size_hint().0);
|
||||
let mut loaded_receivers = Vec::with_capacity(files.size_hint().0);
|
||||
for (file_number, file) in files.enumerate() {
|
||||
let (sender, receiver) = sync_channel(2);
|
||||
loaded_receivers.push(receiver);
|
||||
reader_files.push(ReaderFile {
|
||||
|
@ -146,7 +197,11 @@ impl<'a> FileMerger<'a> {
|
|||
/// Write the merged contents to the output file.
|
||||
pub fn write_all(&mut self, settings: &GlobalSettings) {
|
||||
let mut out = settings.out_writer();
|
||||
while self.write_next(settings, &mut out) {}
|
||||
self.write_all_to(settings, &mut out);
|
||||
}
|
||||
|
||||
pub fn write_all_to(&mut self, settings: &GlobalSettings, out: &mut impl Write) {
|
||||
while self.write_next(settings, out) {}
|
||||
}
|
||||
|
||||
fn write_next(&mut self, settings: &GlobalSettings, out: &mut impl Write) -> bool {
|
||||
|
|
|
@ -95,6 +95,8 @@ static OPT_PARALLEL: &str = "parallel";
|
|||
static OPT_FILES0_FROM: &str = "files0-from";
|
||||
static OPT_BUF_SIZE: &str = "buffer-size";
|
||||
static OPT_TMP_DIR: &str = "temporary-directory";
|
||||
static OPT_COMPRESS_PROG: &str = "compress-program";
|
||||
static OPT_BATCH_SIZE: &str = "batch-size";
|
||||
|
||||
static ARG_FILES: &str = "files";
|
||||
|
||||
|
@ -155,6 +157,8 @@ pub struct GlobalSettings {
|
|||
zero_terminated: bool,
|
||||
buffer_size: usize,
|
||||
tmp_dir: PathBuf,
|
||||
compress_prog: Option<String>,
|
||||
merge_batch_size: usize,
|
||||
}
|
||||
|
||||
impl GlobalSettings {
|
||||
|
@ -223,6 +227,8 @@ impl Default for GlobalSettings {
|
|||
zero_terminated: false,
|
||||
buffer_size: DEFAULT_BUF_SIZE,
|
||||
tmp_dir: PathBuf::new(),
|
||||
compress_prog: None,
|
||||
merge_batch_size: 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -394,9 +400,9 @@ impl<'a> Line<'a> {
|
|||
let line = self.line.replace('\t', ">");
|
||||
writeln!(writer, "{}", line)?;
|
||||
|
||||
let fields = tokenize(&self.line, settings.separator);
|
||||
let fields = tokenize(self.line, settings.separator);
|
||||
for selector in settings.selectors.iter() {
|
||||
let mut selection = selector.get_range(&self.line, Some(&fields));
|
||||
let mut selection = selector.get_range(self.line, Some(&fields));
|
||||
match selector.settings.mode {
|
||||
SortMode::Numeric | SortMode::HumanNumeric => {
|
||||
// find out which range is used for numeric comparisons
|
||||
|
@ -750,7 +756,7 @@ impl FieldSelector {
|
|||
/// Get the selection that corresponds to this selector for the line.
|
||||
/// If needs_fields returned false, tokens may be None.
|
||||
fn get_selection<'a>(&self, line: &'a str, tokens: Option<&[Field]>) -> Selection<'a> {
|
||||
let mut range = &line[self.get_range(&line, tokens)];
|
||||
let mut range = &line[self.get_range(line, tokens)];
|
||||
let num_cache = if self.settings.mode == SortMode::Numeric
|
||||
|| self.settings.mode == SortMode::HumanNumeric
|
||||
{
|
||||
|
@ -840,7 +846,7 @@ impl FieldSelector {
|
|||
|
||||
match resolve_index(line, tokens, &self.from) {
|
||||
Resolution::StartOfChar(from) => {
|
||||
let to = self.to.as_ref().map(|to| resolve_index(line, tokens, &to));
|
||||
let to = self.to.as_ref().map(|to| resolve_index(line, tokens, to));
|
||||
|
||||
let mut range = match to {
|
||||
Some(Resolution::StartOfChar(mut to)) => {
|
||||
|
@ -1076,6 +1082,19 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.takes_value(true)
|
||||
.value_name("DIR"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_COMPRESS_PROG)
|
||||
.long(OPT_COMPRESS_PROG)
|
||||
.help("compress temporary files with PROG, decompress with PROG -d")
|
||||
.long_help("PROG has to take input from stdin and output to stdout")
|
||||
.value_name("PROG")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_BATCH_SIZE)
|
||||
.long(OPT_BATCH_SIZE)
|
||||
.help("Merge at most N_MERGE inputs at once.")
|
||||
.value_name("N_MERGE")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_FILES0_FROM)
|
||||
.long(OPT_FILES0_FROM)
|
||||
|
@ -1165,6 +1184,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.map(PathBuf::from)
|
||||
.unwrap_or_else(env::temp_dir);
|
||||
|
||||
settings.compress_prog = matches.value_of(OPT_COMPRESS_PROG).map(String::from);
|
||||
|
||||
if let Some(n_merge) = matches.value_of(OPT_BATCH_SIZE) {
|
||||
settings.merge_batch_size = n_merge
|
||||
.parse()
|
||||
.unwrap_or_else(|_| crash!(2, "invalid --batch-size argument '{}'", n_merge));
|
||||
}
|
||||
|
||||
settings.zero_terminated = matches.is_present(OPT_ZERO_TERMINATED);
|
||||
settings.merge = matches.is_present(OPT_MERGE);
|
||||
|
||||
|
@ -1230,17 +1257,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
fn output_sorted_lines<'a>(iter: impl Iterator<Item = &'a Line<'a>>, settings: &GlobalSettings) {
|
||||
if settings.unique {
|
||||
print_sorted(
|
||||
iter.dedup_by(|a, b| compare_by(a, b, &settings) == Ordering::Equal),
|
||||
&settings,
|
||||
iter.dedup_by(|a, b| compare_by(a, b, settings) == Ordering::Equal),
|
||||
settings,
|
||||
);
|
||||
} else {
|
||||
print_sorted(iter, &settings);
|
||||
print_sorted(iter, settings);
|
||||
}
|
||||
}
|
||||
|
||||
fn exec(files: &[String], settings: &GlobalSettings) -> i32 {
|
||||
if settings.merge {
|
||||
let mut file_merger = merge::merge(files, settings);
|
||||
let mut file_merger = merge::merge_with_file_limit(files.iter().map(open), settings);
|
||||
file_merger.write_all(settings);
|
||||
} else if settings.check {
|
||||
if files.len() > 1 {
|
||||
|
@ -1250,16 +1277,16 @@ fn exec(files: &[String], settings: &GlobalSettings) -> i32 {
|
|||
} else {
|
||||
let mut lines = files.iter().map(open);
|
||||
|
||||
ext_sort(&mut lines, &settings);
|
||||
ext_sort(&mut lines, settings);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn sort_by<'a>(unsorted: &mut Vec<Line<'a>>, settings: &GlobalSettings) {
|
||||
if settings.stable || settings.unique {
|
||||
unsorted.par_sort_by(|a, b| compare_by(a, b, &settings))
|
||||
unsorted.par_sort_by(|a, b| compare_by(a, b, settings))
|
||||
} else {
|
||||
unsorted.par_sort_unstable_by(|a, b| compare_by(a, b, &settings))
|
||||
unsorted.par_sort_unstable_by(|a, b| compare_by(a, b, settings))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ impl FilterWriter {
|
|||
/// * `filepath` - Path of the output file (forwarded to command as $FILE)
|
||||
fn new(command: &str, filepath: &str) -> FilterWriter {
|
||||
// set $FILE, save previous value (if there was one)
|
||||
let _with_env_var_set = WithEnvVarSet::new("FILE", &filepath);
|
||||
let _with_env_var_set = WithEnvVarSet::new("FILE", filepath);
|
||||
|
||||
let shell_process =
|
||||
Command::new(env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_owned()))
|
||||
|
@ -117,7 +117,7 @@ pub fn instantiate_current_writer(
|
|||
) as Box<dyn Write>),
|
||||
Some(ref filter_command) => BufWriter::new(Box::new(
|
||||
// spawn a shell command and write to it
|
||||
FilterWriter::new(&filter_command, &filename),
|
||||
FilterWriter::new(filter_command, filename),
|
||||
) as Box<dyn Write>),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -477,7 +477,7 @@ impl Stater {
|
|||
Stater::generate_tokens(&Stater::default_format(show_fs, terse, false), use_printf)
|
||||
.unwrap()
|
||||
} else {
|
||||
Stater::generate_tokens(&format_str, use_printf)?
|
||||
Stater::generate_tokens(format_str, use_printf)?
|
||||
};
|
||||
let default_dev_tokens =
|
||||
Stater::generate_tokens(&Stater::default_format(show_fs, terse, true), use_printf)
|
||||
|
|
|
@ -69,9 +69,9 @@ impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
|
|||
|
||||
fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
|
||||
Ok(ProgramOptions {
|
||||
stdin: check_option(&matches, options::INPUT)?,
|
||||
stdout: check_option(&matches, options::OUTPUT)?,
|
||||
stderr: check_option(&matches, options::ERROR)?,
|
||||
stdin: check_option(matches, options::INPUT)?,
|
||||
stdout: check_option(matches, options::OUTPUT)?,
|
||||
stderr: check_option(matches, options::ERROR)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ path = "src/timeout.rs"
|
|||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
getopts = "0.2.18"
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["parse_time", "process", "signals"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
|
|
@ -55,7 +55,7 @@ impl Config {
|
|||
fn from(options: clap::ArgMatches) -> Config {
|
||||
let signal = match options.value_of(options::SIGNAL) {
|
||||
Some(signal_) => {
|
||||
let signal_result = signal_by_name_or_value(&signal_);
|
||||
let signal_result = signal_by_name_or_value(signal_);
|
||||
match signal_result {
|
||||
None => {
|
||||
unreachable!("invalid signal '{}'", signal_);
|
||||
|
@ -67,7 +67,7 @@ impl Config {
|
|||
};
|
||||
|
||||
let kill_after: Duration = match options.value_of(options::KILL_AFTER) {
|
||||
Some(time) => uucore::parse_time::from_str(&time).unwrap(),
|
||||
Some(time) => uucore::parse_time::from_str(time).unwrap(),
|
||||
None => Duration::new(0, 0),
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/tty.rs"
|
|||
[dependencies]
|
||||
clap = "2.33"
|
||||
libc = "0.2.42"
|
||||
atty = "0.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ extern crate uucore;
|
|||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::ffi::CStr;
|
||||
use uucore::fs::is_stdin_interactive;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static ABOUT: &str = "Print the file name of the terminal connected to standard input.";
|
||||
|
@ -67,7 +66,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
if is_stdin_interactive() {
|
||||
if atty::is(atty::Stream::Stdin) {
|
||||
libc::EXIT_SUCCESS
|
||||
} else {
|
||||
libc::EXIT_FAILURE
|
||||
|
|
|
@ -374,8 +374,8 @@ fn wc(inputs: Vec<Input>, settings: &Settings) -> Result<(), u32> {
|
|||
let num_inputs = inputs.len();
|
||||
|
||||
for input in &inputs {
|
||||
let word_count = word_count_from_input(&input, settings).unwrap_or_else(|err| {
|
||||
show_error(&input, err);
|
||||
let word_count = word_count_from_input(input, settings).unwrap_or_else(|err| {
|
||||
show_error(input, err);
|
||||
error_count += 1;
|
||||
WordCount::default()
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (vars) Passwd cstr fnam gecos ngroups
|
||||
// spell-checker:ignore (vars) Passwd cstr fnam gecos ngroups egid
|
||||
|
||||
//! Get password/group file entry
|
||||
//!
|
||||
|
@ -72,6 +72,45 @@ pub fn get_groups() -> IOResult<Vec<gid_t>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The list of group IDs returned from GNU's `groups` and GNU's `id --groups`
|
||||
/// starts with the effective group ID (egid).
|
||||
/// This is a wrapper for `get_groups()` to mimic this behavior.
|
||||
///
|
||||
/// If `arg_id` is `None` (default), `get_groups_gnu` moves the effective
|
||||
/// group id (egid) to the first entry in the returned Vector.
|
||||
/// If `arg_id` is `Some(x)`, `get_groups_gnu` moves the id with value `x`
|
||||
/// to the first entry in the returned Vector. This might be necessary
|
||||
/// for `id --groups --real` if `gid` and `egid` are not equal.
|
||||
///
|
||||
/// From: https://www.man7.org/linux/man-pages/man3/getgroups.3p.html
|
||||
/// As implied by the definition of supplementary groups, the
|
||||
/// effective group ID may appear in the array returned by
|
||||
/// getgroups() or it may be returned only by getegid(). Duplication
|
||||
/// may exist, but the application needs to call getegid() to be sure
|
||||
/// of getting all of the information. Various implementation
|
||||
/// variations and administrative sequences cause the set of groups
|
||||
/// appearing in the result of getgroups() to vary in order and as to
|
||||
/// whether the effective group ID is included, even when the set of
|
||||
/// groups is the same (in the mathematical sense of ``set''). (The
|
||||
/// history of a process and its parents could affect the details of
|
||||
/// the result.)
|
||||
#[cfg(all(unix, feature = "process"))]
|
||||
pub fn get_groups_gnu(arg_id: Option<u32>) -> IOResult<Vec<gid_t>> {
|
||||
let groups = get_groups()?;
|
||||
let egid = arg_id.unwrap_or_else(crate::features::process::getegid);
|
||||
Ok(sort_groups(groups, egid))
|
||||
}
|
||||
|
||||
fn sort_groups(mut groups: Vec<gid_t>, egid: gid_t) -> Vec<gid_t> {
|
||||
if let Some(index) = groups.iter().position(|&x| x == egid) {
|
||||
groups[..=index].rotate_right(1);
|
||||
} else {
|
||||
groups.insert(0, egid);
|
||||
}
|
||||
groups
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Passwd {
|
||||
inner: passwd,
|
||||
}
|
||||
|
@ -268,3 +307,27 @@ pub fn usr2uid(name: &str) -> IOResult<uid_t> {
|
|||
pub fn grp2gid(name: &str) -> IOResult<gid_t> {
|
||||
Group::locate(name).map(|p| p.gid())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sort_groups() {
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 4), vec![4, 1, 2, 3]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 3), vec![3, 1, 2]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 2), vec![2, 1, 3]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 1), vec![1, 2, 3]);
|
||||
assert_eq!(sort_groups(vec![1, 2, 3], 0), vec![0, 1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entries_get_groups_gnu() {
|
||||
if let Ok(mut groups) = get_groups() {
|
||||
if let Some(last) = groups.pop() {
|
||||
groups.insert(0, last);
|
||||
assert_eq!(get_groups_gnu(Some(last)).unwrap(), groups);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,51 +225,6 @@ pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) ->
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn is_stdin_interactive() -> bool {
|
||||
unsafe { libc::isatty(libc::STDIN_FILENO) == 1 }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn is_stdin_interactive() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_stdin_interactive() -> bool {
|
||||
termion::is_tty(&io::stdin())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn is_stdout_interactive() -> bool {
|
||||
unsafe { libc::isatty(libc::STDOUT_FILENO) == 1 }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn is_stdout_interactive() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_stdout_interactive() -> bool {
|
||||
termion::is_tty(&io::stdout())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn is_stderr_interactive() -> bool {
|
||||
unsafe { libc::isatty(libc::STDERR_FILENO) == 1 }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn is_stderr_interactive() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_stderr_interactive() -> bool {
|
||||
termion::is_tty(&io::stderr())
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[allow(unused_variables)]
|
||||
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
|
||||
|
|
|
@ -207,7 +207,7 @@ impl Utmpx {
|
|||
flags: AI_CANONNAME,
|
||||
..AddrInfoHints::default()
|
||||
};
|
||||
let sockets = getaddrinfo(Some(&hostname), None, Some(hints))
|
||||
let sockets = getaddrinfo(Some(hostname), None, Some(hints))
|
||||
.unwrap()
|
||||
.collect::<IOResult<Vec<_>>>()?;
|
||||
for socket in sockets {
|
||||
|
|
|
@ -438,7 +438,7 @@ fn test_domain_socket() {
|
|||
let child = new_ucmd!().args(&[socket_path]).run_no_wait();
|
||||
barrier.wait();
|
||||
let stdout = &child.wait_with_output().unwrap().stdout;
|
||||
let output = String::from_utf8_lossy(&stdout);
|
||||
let output = String::from_utf8_lossy(stdout);
|
||||
assert_eq!("a\tb", output);
|
||||
|
||||
thread.join().unwrap();
|
||||
|
|
|
@ -618,7 +618,7 @@ fn test_cp_deref() {
|
|||
// Check the content of the destination file that was copied.
|
||||
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
||||
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
||||
assert_eq!(at.read(&path_to_check), "Hello, World!\n");
|
||||
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
||||
}
|
||||
#[test]
|
||||
fn test_cp_no_deref() {
|
||||
|
@ -655,7 +655,7 @@ fn test_cp_no_deref() {
|
|||
// Check the content of the destination file that was copied.
|
||||
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
||||
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
||||
assert_eq!(at.read(&path_to_check), "Hello, World!\n");
|
||||
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -823,7 +823,7 @@ fn test_cp_deref_folder_to_folder() {
|
|||
|
||||
// Check the content of the symlink
|
||||
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
||||
assert_eq!(at.read(&path_to_check), "Hello, World!\n");
|
||||
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -923,7 +923,7 @@ fn test_cp_no_deref_folder_to_folder() {
|
|||
|
||||
// Check the content of the symlink
|
||||
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
||||
assert_eq!(at.read(&path_to_check), "Hello, World!\n");
|
||||
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -187,11 +187,10 @@ fn test_change_directory() {
|
|||
.arg(&temporary_path)
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
assert_eq!(
|
||||
out.lines()
|
||||
.any(|line| line.ends_with(temporary_path.file_name().unwrap().to_str().unwrap())),
|
||||
false
|
||||
);
|
||||
|
||||
assert!(!out
|
||||
.lines()
|
||||
.any(|line| line.ends_with(temporary_path.file_name().unwrap().to_str().unwrap())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,41 +1,56 @@
|
|||
use crate::common::util::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_groups() {
|
||||
let result = new_ucmd!().run();
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
if is_ci() && result.stdout_str().trim().is_empty() {
|
||||
// In the CI, some server are failing to return the group.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
if !is_ci() {
|
||||
new_ucmd!().succeeds().stdout_is(expected_result(&[]));
|
||||
} else {
|
||||
// TODO: investigate how this could be tested in CI
|
||||
// stderr = groups: cannot find name for group ID 116
|
||||
println!("test skipped:");
|
||||
}
|
||||
result.success();
|
||||
assert!(!result.stdout_str().trim().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_groups_arg() {
|
||||
// get the username with the "id -un" command
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-un").run();
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let s1 = String::from(result.stdout_str().trim());
|
||||
if is_ci() && s1.parse::<f64>().is_ok() {
|
||||
// In the CI, some server are failing to return id -un.
|
||||
// So, if we are getting a uid, just skip this test
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
#[cfg(unix)]
|
||||
#[ignore = "fixme: 'groups USERNAME' needs more debugging"]
|
||||
fn test_groups_username() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let whoami_result = scene.cmd("whoami").run();
|
||||
|
||||
let username = if whoami_result.succeeded() {
|
||||
whoami_result.stdout_move_str()
|
||||
} else if is_ci() {
|
||||
String::from("docker")
|
||||
} else {
|
||||
println!("test skipped:");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
result.success();
|
||||
assert!(!result.stdout_str().is_empty());
|
||||
let username = result.stdout_str().trim();
|
||||
// TODO: stdout should be in the form: "username : group1 group2 group3"
|
||||
|
||||
// call groups with the user name to check that we
|
||||
// are getting something
|
||||
new_ucmd!().arg(username).succeeds();
|
||||
assert!(!result.stdout_str().is_empty());
|
||||
scene
|
||||
.ucmd()
|
||||
.arg(&username)
|
||||
.succeeds()
|
||||
.stdout_is(expected_result(&[&username]));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn expected_result(args: &[&str]) -> String {
|
||||
// We want to use GNU id. On most linux systems, this is "id", but on
|
||||
// bsd-like systems (e.g. FreeBSD, MacOS), it is commonly "gid".
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
let util_name = "id";
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let util_name = "gid";
|
||||
|
||||
TestScenario::new(util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
.args(args)
|
||||
.args(&["-Gn"])
|
||||
.succeeds()
|
||||
.stdout_move_str()
|
||||
}
|
||||
|
|
|
@ -104,19 +104,23 @@ fn test_id_group() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||
fn test_id_groups() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let result = scene.ucmd().arg("-G").succeeds();
|
||||
let groups = result.stdout_str().trim().split_whitespace();
|
||||
for s in groups {
|
||||
assert!(s.parse::<f64>().is_ok());
|
||||
}
|
||||
|
||||
let result = scene.ucmd().arg("--groups").succeeds();
|
||||
let groups = result.stdout_str().trim().split_whitespace();
|
||||
for s in groups {
|
||||
assert!(s.parse::<f64>().is_ok());
|
||||
for g_flag in &["-G", "--groups"] {
|
||||
scene
|
||||
.ucmd()
|
||||
.arg(g_flag)
|
||||
.succeeds()
|
||||
.stdout_is(expected_result(&[g_flag], false));
|
||||
for &r_flag in &["-r", "--real"] {
|
||||
let args = [g_flag, r_flag];
|
||||
scene
|
||||
.ucmd()
|
||||
.args(&args)
|
||||
.succeeds()
|
||||
.stdout_is(expected_result(&args, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,3 +171,32 @@ fn test_id_password_style() {
|
|||
|
||||
assert!(result.stdout_str().starts_with(&username));
|
||||
}
|
||||
|
||||
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
|
||||
fn expected_result(args: &[&str], exp_fail: bool) -> String {
|
||||
#[cfg(target_os = "linux")]
|
||||
let util_name = util_name!();
|
||||
#[cfg(target_vendor = "apple")]
|
||||
let util_name = format!("g{}", util_name!());
|
||||
|
||||
let result = if !exp_fail {
|
||||
TestScenario::new(&util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
.args(args)
|
||||
.succeeds()
|
||||
.stdout_move_str()
|
||||
} else {
|
||||
TestScenario::new(&util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
.args(args)
|
||||
.fails()
|
||||
.stderr_move_str()
|
||||
};
|
||||
return if cfg!(target_os = "macos") && result.starts_with("gid") {
|
||||
result[1..].to_string()
|
||||
} else {
|
||||
result
|
||||
};
|
||||
}
|
||||
|
|
|
@ -398,7 +398,7 @@ fn test_ls_long_formats() {
|
|||
.arg("--author")
|
||||
.arg("test-long-formats")
|
||||
.succeeds();
|
||||
assert!(re_three.is_match(&result.stdout_str()));
|
||||
assert!(re_three.is_match(result.stdout_str()));
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
|
@ -701,20 +701,20 @@ fn test_ls_styles() {
|
|||
.arg("-l")
|
||||
.arg("--time-style=full-iso")
|
||||
.succeeds();
|
||||
assert!(re_full.is_match(&result.stdout_str()));
|
||||
assert!(re_full.is_match(result.stdout_str()));
|
||||
//long-iso
|
||||
let result = scene
|
||||
.ucmd()
|
||||
.arg("-l")
|
||||
.arg("--time-style=long-iso")
|
||||
.succeeds();
|
||||
assert!(re_long.is_match(&result.stdout_str()));
|
||||
assert!(re_long.is_match(result.stdout_str()));
|
||||
//iso
|
||||
let result = scene.ucmd().arg("-l").arg("--time-style=iso").succeeds();
|
||||
assert!(re_iso.is_match(&result.stdout_str()));
|
||||
assert!(re_iso.is_match(result.stdout_str()));
|
||||
//locale
|
||||
let result = scene.ucmd().arg("-l").arg("--time-style=locale").succeeds();
|
||||
assert!(re_locale.is_match(&result.stdout_str()));
|
||||
assert!(re_locale.is_match(result.stdout_str()));
|
||||
|
||||
//Overwrite options tests
|
||||
let result = scene
|
||||
|
@ -723,19 +723,19 @@ fn test_ls_styles() {
|
|||
.arg("--time-style=long-iso")
|
||||
.arg("--time-style=iso")
|
||||
.succeeds();
|
||||
assert!(re_iso.is_match(&result.stdout_str()));
|
||||
assert!(re_iso.is_match(result.stdout_str()));
|
||||
let result = scene
|
||||
.ucmd()
|
||||
.arg("--time-style=iso")
|
||||
.arg("--full-time")
|
||||
.succeeds();
|
||||
assert!(re_full.is_match(&result.stdout_str()));
|
||||
assert!(re_full.is_match(result.stdout_str()));
|
||||
let result = scene
|
||||
.ucmd()
|
||||
.arg("--full-time")
|
||||
.arg("--time-style=iso")
|
||||
.succeeds();
|
||||
assert!(re_iso.is_match(&result.stdout_str()));
|
||||
assert!(re_iso.is_match(result.stdout_str()));
|
||||
|
||||
let result = scene
|
||||
.ucmd()
|
||||
|
@ -743,7 +743,7 @@ fn test_ls_styles() {
|
|||
.arg("--time-style=iso")
|
||||
.arg("--full-time")
|
||||
.succeeds();
|
||||
assert!(re_full.is_match(&result.stdout_str()));
|
||||
assert!(re_full.is_match(result.stdout_str()));
|
||||
|
||||
let result = scene
|
||||
.ucmd()
|
||||
|
@ -751,7 +751,7 @@ fn test_ls_styles() {
|
|||
.arg("-x")
|
||||
.arg("-l")
|
||||
.succeeds();
|
||||
assert!(re_full.is_match(&result.stdout_str()));
|
||||
assert!(re_full.is_match(result.stdout_str()));
|
||||
|
||||
at.touch("test2");
|
||||
let result = scene.ucmd().arg("--full-time").arg("-x").succeeds();
|
||||
|
@ -1143,7 +1143,7 @@ fn test_ls_indicator_style() {
|
|||
for opt in options {
|
||||
scene
|
||||
.ucmd()
|
||||
.arg(format!("{}", opt))
|
||||
.arg(opt.to_string())
|
||||
.succeeds()
|
||||
.stdout_contains(&"/");
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ fn test_posix_mode() {
|
|||
|
||||
// fail on long path
|
||||
new_ucmd!()
|
||||
.args(&["-p", &"dir".repeat(libc::PATH_MAX as usize + 1).as_str()])
|
||||
.args(&["-p", "dir".repeat(libc::PATH_MAX as usize + 1).as_str()])
|
||||
.fails()
|
||||
.no_stdout();
|
||||
|
||||
|
@ -46,7 +46,7 @@ fn test_posix_mode() {
|
|||
new_ucmd!()
|
||||
.args(&[
|
||||
"-p",
|
||||
&format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
|
||||
format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
|
||||
])
|
||||
.fails()
|
||||
.no_stdout();
|
||||
|
@ -76,7 +76,7 @@ fn test_posix_special() {
|
|||
|
||||
// fail on long path
|
||||
new_ucmd!()
|
||||
.args(&["-P", &"dir".repeat(libc::PATH_MAX as usize + 1).as_str()])
|
||||
.args(&["-P", "dir".repeat(libc::PATH_MAX as usize + 1).as_str()])
|
||||
.fails()
|
||||
.no_stdout();
|
||||
|
||||
|
@ -84,7 +84,7 @@ fn test_posix_special() {
|
|||
new_ucmd!()
|
||||
.args(&[
|
||||
"-P",
|
||||
&format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
|
||||
format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
|
||||
])
|
||||
.fails()
|
||||
.no_stdout();
|
||||
|
@ -117,7 +117,7 @@ fn test_posix_all() {
|
|||
.args(&[
|
||||
"-p",
|
||||
"-P",
|
||||
&"dir".repeat(libc::PATH_MAX as usize + 1).as_str(),
|
||||
"dir".repeat(libc::PATH_MAX as usize + 1).as_str(),
|
||||
])
|
||||
.fails()
|
||||
.no_stdout();
|
||||
|
@ -127,7 +127,7 @@ fn test_posix_all() {
|
|||
.args(&[
|
||||
"-p",
|
||||
"-P",
|
||||
&format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
|
||||
format!("dir/{}", "file".repeat(libc::FILENAME_MAX as usize + 1)).as_str(),
|
||||
])
|
||||
.fails()
|
||||
.no_stdout();
|
||||
|
|
|
@ -102,6 +102,8 @@ fn expected_result(args: &[&str]) -> String {
|
|||
#[cfg(target_vendor = "apple")]
|
||||
let util_name = format!("g{}", util_name!());
|
||||
|
||||
// note: clippy::needless_borrow *false positive*
|
||||
#[allow(clippy::needless_borrow)]
|
||||
TestScenario::new(&util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
|
|
|
@ -792,3 +792,59 @@ fn test_nonexistent_file() {
|
|||
fn test_blanks() {
|
||||
test_helper("blanks", &["-b", "--ignore-blanks"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sort_multiple() {
|
||||
new_ucmd!()
|
||||
.args(&["no_trailing_newline1.txt", "no_trailing_newline2.txt"])
|
||||
.succeeds()
|
||||
.stdout_is("a\nb\nb\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sort_empty_chunk() {
|
||||
new_ucmd!()
|
||||
.args(&["-S", "40B"])
|
||||
.pipe_in("a\na\n")
|
||||
.succeeds()
|
||||
.stdout_is("a\na\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_compress() {
|
||||
new_ucmd!()
|
||||
.args(&[
|
||||
"ext_sort.txt",
|
||||
"-n",
|
||||
"--compress-program",
|
||||
"gzip",
|
||||
"-S",
|
||||
"10",
|
||||
])
|
||||
.succeeds()
|
||||
.stdout_only_fixture("ext_sort.expected");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compress_fail() {
|
||||
new_ucmd!()
|
||||
.args(&[
|
||||
"ext_sort.txt",
|
||||
"-n",
|
||||
"--compress-program",
|
||||
"nonexistent-program",
|
||||
"-S",
|
||||
"10",
|
||||
])
|
||||
.fails()
|
||||
.stderr_only("sort: couldn't execute compress program: errno 2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_batches() {
|
||||
new_ucmd!()
|
||||
.args(&["ext_sort.txt", "-n", "-S", "150B"])
|
||||
.succeeds()
|
||||
.stdout_only_fixture("ext_sort.expected");
|
||||
}
|
||||
|
|
|
@ -313,6 +313,8 @@ fn expected_result(args: &[&str]) -> String {
|
|||
#[cfg(target_vendor = "apple")]
|
||||
let util_name = format!("g{}", util_name!());
|
||||
|
||||
// note: clippy::needless_borrow *false positive*
|
||||
#[allow(clippy::needless_borrow)]
|
||||
TestScenario::new(&util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
|
|
|
@ -13,6 +13,8 @@ fn test_users_check_name() {
|
|||
#[cfg(target_vendor = "apple")]
|
||||
let util_name = format!("g{}", util_name!());
|
||||
|
||||
// note: clippy::needless_borrow *false positive*
|
||||
#[allow(clippy::needless_borrow)]
|
||||
let expected = TestScenario::new(&util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
|
|
|
@ -238,6 +238,8 @@ fn expected_result(args: &[&str]) -> String {
|
|||
#[cfg(target_vendor = "apple")]
|
||||
let util_name = format!("g{}", util_name!());
|
||||
|
||||
// note: clippy::needless_borrow *false positive*
|
||||
#[allow(clippy::needless_borrow)]
|
||||
TestScenario::new(&util_name)
|
||||
.cmd_keepenv(util_name)
|
||||
.env("LANGUAGE", "C")
|
||||
|
|
|
@ -625,11 +625,20 @@ impl AtPath {
|
|||
// Source:
|
||||
// http://stackoverflow.com/questions/31439011/getfinalpathnamebyhandle-without-prepended
|
||||
let prefix = "\\\\?\\";
|
||||
// FixME: replace ...
|
||||
#[allow(clippy::manual_strip)]
|
||||
if s.starts_with(prefix) {
|
||||
String::from(&s[prefix.len()..])
|
||||
} else {
|
||||
s
|
||||
}
|
||||
// ... with ...
|
||||
// if let Some(stripped) = s.strip_prefix(prefix) {
|
||||
// String::from(stripped)
|
||||
// } else {
|
||||
// s
|
||||
// }
|
||||
// ... when using MSRV with stabilized `strip_prefix()`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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