Merge branch 'master' into id_zero_2351

This commit is contained in:
Jan Scheer 2021-06-13 11:14:56 +02:00
commit 052202ca19
136 changed files with 2205 additions and 1916 deletions

View file

@ -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,12 +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})"
UTILITY_LIST="id" # TODO: remove after debugging
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:
@ -607,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

View file

@ -97,6 +97,9 @@ Michael Debertol
Michael Gehring
Michael
Gehring
Mitchell Mebane
Mitchell
Mebane
Morten Olsen Lysgaard
Morten
Olsen

View file

@ -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

18
Cargo.lock generated
View file

@ -6,16 +6,6 @@ version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
[[package]]
name = "advapi32-sys"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
@ -1772,6 +1762,7 @@ dependencies = [
name = "uu_cat"
version = "0.0.6"
dependencies = [
"atty",
"clap",
"nix 0.20.0",
"thiserror",
@ -1784,6 +1775,7 @@ dependencies = [
name = "uu_chgrp"
version = "0.0.6"
dependencies = [
"clap",
"uucore",
"uucore_procs",
"walkdir",
@ -1872,6 +1864,7 @@ dependencies = [
name = "uu_cut"
version = "0.0.6"
dependencies = [
"atty",
"bstr",
"clap",
"memchr 2.4.0",
@ -2262,6 +2255,7 @@ dependencies = [
name = "uu_nohup"
version = "0.0.6"
dependencies = [
"atty",
"clap",
"libc",
"uucore",
@ -2420,6 +2414,7 @@ dependencies = [
"uucore",
"uucore_procs",
"walkdir",
"winapi 0.3.9",
]
[[package]]
@ -2601,7 +2596,6 @@ name = "uu_timeout"
version = "0.0.6"
dependencies = [
"clap",
"getopts",
"libc",
"uucore",
"uucore_procs",
@ -2659,6 +2653,7 @@ dependencies = [
name = "uu_tty"
version = "0.0.6"
dependencies = [
"atty",
"clap",
"libc",
"uucore",
@ -2750,7 +2745,6 @@ dependencies = [
name = "uu_whoami"
version = "0.0.6"
dependencies = [
"advapi32-sys",
"clap",
"uucore",
"uucore_procs",

View file

@ -351,7 +351,7 @@ time = "0.1"
unindent = "0.1"
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"

View file

@ -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:

View file

@ -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(

View file

@ -54,15 +54,13 @@ impl Config {
None => None,
};
let cols = match options.value_of(options::WRAP) {
Some(num) => match num.parse::<usize>() {
Ok(n) => Some(n),
Err(e) => {
return Err(format!("Invalid wrap size: {}: {}", num, e));
}
},
None => None,
};
let cols = options
.value_of(options::WRAP)
.map(|num| {
num.parse::<usize>()
.map_err(|e| format!("Invalid wrap size: {}: {}", num, e))
})
.transpose()?;
Ok(Config {
decode: options.is_present(options::DECODE),

View file

@ -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(

View file

@ -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
@ -118,14 +118,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fn basename(fullname: &str, suffix: &str) -> String {
// Remove all platform-specific path separators from the end
let mut path: String = fullname
.chars()
.rev()
.skip_while(|&ch| is_separator(ch))
.collect();
// Undo reverse
path = path.chars().rev().collect();
let path = fullname.trim_end_matches(is_separator);
// Convert to path buffer and get last path component
let pb = PathBuf::from(path);

View file

@ -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" }

View file

@ -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;
}

View file

@ -15,6 +15,7 @@ edition = "2018"
path = "src/chgrp.rs"
[dependencies]
clap = "2.33"
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
walkdir = "2.2"

View file

@ -14,6 +14,8 @@ use uucore::fs::resolve_relative_path;
use uucore::libc::gid_t;
use uucore::perms::{wrap_chgrp, Verbosity};
use clap::{App, Arg};
extern crate walkdir;
use walkdir::WalkDir;
@ -24,76 +26,194 @@ use std::os::unix::fs::MetadataExt;
use std::path::Path;
use uucore::InvalidEncodingHandling;
static SYNTAX: &str =
"chgrp [OPTION]... GROUP FILE...\n or : chgrp [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &str = "Change the group of each FILE to GROUP.";
static ABOUT: &str = "Change the group of each FILE to GROUP.";
static VERSION: &str = env!("CARGO_PKG_VERSION");
pub mod options {
pub mod verbosity {
pub static CHANGES: &str = "changes";
pub static QUIET: &str = "quiet";
pub static SILENT: &str = "silent";
pub static VERBOSE: &str = "verbose";
}
pub mod preserve_root {
pub static PRESERVE: &str = "preserve-root";
pub static NO_PRESERVE: &str = "no-preserve-root";
}
pub mod dereference {
pub static DEREFERENCE: &str = "dereference";
pub static NO_DEREFERENCE: &str = "no-dereference";
}
pub static RECURSIVE: &str = "recursive";
pub mod traverse {
pub static TRAVERSE: &str = "H";
pub static NO_TRAVERSE: &str = "P";
pub static EVERY: &str = "L";
}
pub static REFERENCE: &str = "reference";
pub static ARG_GROUP: &str = "GROUP";
pub static ARG_FILES: &str = "FILE";
}
const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;
fn get_usage() -> String {
format!(
"{0} [OPTION]... GROUP FILE...\n {0} [OPTION]... --reference=RFILE FILE...",
executable!()
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
let mut opts = app!(SYNTAX, SUMMARY, "");
opts.optflag("c",
"changes",
"like verbose but report only when a change is made")
.optflag("f", "silent", "")
.optflag("", "quiet", "suppress most error messages")
.optflag("v",
"verbose",
"output a diagnostic for every file processed")
.optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
.optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")
.optflag("",
"no-preserve-root",
"do not treat '/' specially (the default)")
.optflag("", "preserve-root", "fail to operate recursively on '/'")
.optopt("",
"reference",
"use RFILE's owner and group rather than specifying OWNER:GROUP values",
"RFILE")
.optflag("R",
"recursive",
"operate on files and directories recursively")
.optflag("H",
"",
"if a command line argument is a symbolic link to a directory, traverse it")
.optflag("L",
"",
"traverse every symbolic link to a directory encountered")
.optflag("P", "", "do not traverse any symbolic links (default)");
let usage = get_usage();
let mut bit_flag = FTS_PHYSICAL;
let mut preserve_root = false;
let mut derefer = -1;
let flags: &[char] = &['H', 'L', 'P'];
for opt in &args {
match opt.as_str() {
// If more than one is specified, only the final one takes effect.
s if s.contains(flags) => {
if let Some(idx) = s.rfind(flags) {
match s.chars().nth(idx).unwrap() {
'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
'L' => bit_flag = FTS_LOGICAL,
'P' => bit_flag = FTS_PHYSICAL,
_ => (),
}
}
}
"--no-preserve-root" => preserve_root = false,
"--preserve-root" => preserve_root = true,
"--dereference" => derefer = 1,
"--no-dereference" => derefer = 0,
_ => (),
let mut app = App::new(executable!())
.version(VERSION)
.about(ABOUT)
.usage(&usage[..])
.arg(
Arg::with_name(options::verbosity::CHANGES)
.short("c")
.long(options::verbosity::CHANGES)
.help("like verbose but report only when a change is made"),
)
.arg(
Arg::with_name(options::verbosity::SILENT)
.short("f")
.long(options::verbosity::SILENT),
)
.arg(
Arg::with_name(options::verbosity::QUIET)
.long(options::verbosity::QUIET)
.help("suppress most error messages"),
)
.arg(
Arg::with_name(options::verbosity::VERBOSE)
.short("v")
.long(options::verbosity::VERBOSE)
.help("output a diagnostic for every file processed"),
)
.arg(
Arg::with_name(options::dereference::DEREFERENCE)
.long(options::dereference::DEREFERENCE),
)
.arg(
Arg::with_name(options::dereference::NO_DEREFERENCE)
.short("h")
.long(options::dereference::NO_DEREFERENCE)
.help(
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
),
)
.arg(
Arg::with_name(options::preserve_root::PRESERVE)
.long(options::preserve_root::PRESERVE)
.help("fail to operate recursively on '/'"),
)
.arg(
Arg::with_name(options::preserve_root::NO_PRESERVE)
.long(options::preserve_root::NO_PRESERVE)
.help("do not treat '/' specially (the default)"),
)
.arg(
Arg::with_name(options::REFERENCE)
.long(options::REFERENCE)
.value_name("RFILE")
.help("use RFILE's group rather than specifying GROUP values")
.takes_value(true)
.multiple(false),
)
.arg(
Arg::with_name(options::RECURSIVE)
.short("R")
.long(options::RECURSIVE)
.help("operate on files and directories recursively"),
)
.arg(
Arg::with_name(options::traverse::TRAVERSE)
.short(options::traverse::TRAVERSE)
.help("if a command line argument is a symbolic link to a directory, traverse it"),
)
.arg(
Arg::with_name(options::traverse::NO_TRAVERSE)
.short(options::traverse::NO_TRAVERSE)
.help("do not traverse any symbolic links (default)")
.overrides_with_all(&[options::traverse::TRAVERSE, options::traverse::EVERY]),
)
.arg(
Arg::with_name(options::traverse::EVERY)
.short(options::traverse::EVERY)
.help("traverse every symbolic link to a directory encountered"),
);
// we change the positional args based on whether
// --reference was used.
let mut reference = false;
let mut help = false;
// stop processing options on --
for arg in args.iter().take_while(|s| *s != "--") {
if arg.starts_with("--reference=") || arg == "--reference" {
reference = true;
} else if arg == "--help" {
// we stop processing once we see --help,
// as it doesn't matter if we've seen reference or not
help = true;
break;
}
}
let matches = opts.parse(args);
let recursive = matches.opt_present("recursive");
if help || !reference {
// add both positional arguments
app = app.arg(
Arg::with_name(options::ARG_GROUP)
.value_name(options::ARG_GROUP)
.required(true)
.takes_value(true)
.multiple(false),
)
}
app = app.arg(
Arg::with_name(options::ARG_FILES)
.value_name(options::ARG_FILES)
.multiple(true)
.takes_value(true)
.required(true)
.min_values(1),
);
let matches = app.get_matches_from(args);
/* Get the list of files */
let files: Vec<String> = matches
.values_of(options::ARG_FILES)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
let preserve_root = matches.is_present(options::preserve_root::PRESERVE);
let mut derefer = if matches.is_present(options::dereference::DEREFERENCE) {
1
} else if matches.is_present(options::dereference::NO_DEREFERENCE) {
0
} else {
-1
};
let mut bit_flag = if matches.is_present(options::traverse::TRAVERSE) {
FTS_COMFOLLOW | FTS_PHYSICAL
} else if matches.is_present(options::traverse::EVERY) {
FTS_LOGICAL
} else {
FTS_PHYSICAL
};
let recursive = matches.is_present(options::RECURSIVE);
if recursive {
if bit_flag == FTS_PHYSICAL {
if derefer == 1 {
@ -106,27 +226,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
bit_flag = FTS_PHYSICAL;
}
let verbosity = if matches.opt_present("changes") {
let verbosity = if matches.is_present(options::verbosity::CHANGES) {
Verbosity::Changes
} else if matches.opt_present("silent") || matches.opt_present("quiet") {
} else if matches.is_present(options::verbosity::SILENT)
|| matches.is_present(options::verbosity::QUIET)
{
Verbosity::Silent
} else if matches.opt_present("verbose") {
} else if matches.is_present(options::verbosity::VERBOSE) {
Verbosity::Verbose
} else {
Verbosity::Normal
};
if matches.free.is_empty() {
show_usage_error!("missing operand");
return 1;
} else if matches.free.len() < 2 && !matches.opt_present("reference") {
show_usage_error!("missing operand after {}", matches.free[0]);
return 1;
}
let dest_gid: gid_t;
let mut files;
if let Some(file) = matches.opt_str("reference") {
let dest_gid: u32;
if let Some(file) = matches.value_of(options::REFERENCE) {
match fs::metadata(&file) {
Ok(meta) => {
dest_gid = meta.gid();
@ -136,19 +249,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
return 1;
}
}
files = matches.free;
} else {
match entries::grp2gid(&matches.free[0]) {
let group = matches.value_of(options::ARG_GROUP).unwrap_or_default();
match entries::grp2gid(group) {
Ok(g) => {
dest_gid = g;
}
_ => {
show_error!("invalid group: {}", matches.free[0].as_str());
show_error!("invalid group: {}", group);
return 1;
}
}
files = matches.free;
files.remove(0);
}
let executor = Chgrper {

View file

@ -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);
}
}
}

View file

@ -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;
@ -278,37 +278,25 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
let usr_only = args.len() == 1 && !args[0].is_empty();
let grp_only = args.len() == 2 && args[0].is_empty();
let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty();
if usr_only {
Ok((
Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)),
}),
None,
))
} else if grp_only {
Ok((
None,
Some(match Group::locate(args[1]) {
Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)),
}),
))
} else if usr_grp {
Ok((
Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)),
}),
Some(match Group::locate(args[1]) {
Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)),
}),
))
let uid = if usr_only || usr_grp {
Some(
Passwd::locate(args[0])
.map_err(|_| format!("invalid user: {}", spec))?
.uid(),
)
} else {
Ok((None, None))
}
None
};
let gid = if grp_only || usr_grp {
Some(
Group::locate(args[1])
.map_err(|_| format!("invalid group: {}", spec))?
.gid(),
)
} else {
None
};
Ok((uid, gid))
}
enum IfFrom {
@ -497,3 +485,17 @@ impl Chowner {
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_parse_spec() {
assert_eq!(parse_spec(":"), Ok((None, None)));
assert!(parse_spec("::")
.err()
.unwrap()
.starts_with("invalid group: "));
}
}

View file

@ -28,6 +28,7 @@ mod options {
pub const GROUP: &str = "group";
pub const GROUPS: &str = "groups";
pub const USERSPEC: &str = "userspec";
pub const COMMAND: &str = "command";
}
pub fn uumain(args: impl uucore::Args) -> i32 {
@ -39,7 +40,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.version(crate_version!())
.about(ABOUT)
.usage(SYNTAX)
.arg(Arg::with_name(options::NEWROOT).hidden(true).required(true))
.arg(
Arg::with_name(options::NEWROOT)
.hidden(true)
.required(true)
.index(1),
)
.arg(
Arg::with_name(options::USER)
.short("u")
@ -71,6 +77,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
)
.value_name("USER:GROUP"),
)
.arg(
Arg::with_name(options::COMMAND)
.hidden(true)
.multiple(true)
.index(2),
)
.get_matches_from(args);
let default_shell: &'static str = "/bin/sh";
@ -94,7 +106,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
);
}
let command: Vec<&str> = match matches.args.len() {
let commands = match matches.values_of(options::COMMAND) {
Some(v) => v.collect(),
None => vec![],
};
// TODO: refactor the args and command matching
// See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967
let command: Vec<&str> = match commands.len() {
1 => {
let shell: &str = match user_shell {
Err(_) => default_shell,
@ -102,17 +121,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
};
vec![shell, default_option]
}
_ => {
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
}
_ => commands,
};
set_context(&newroot, &matches);
set_context(newroot, &matches);
let pstatus = Command::new(command[0])
.args(&command[1..])
@ -132,7 +144,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)

View file

@ -160,18 +160,14 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
let mut bytes = init_byte_array();
loop {
match rd.read(&mut bytes) {
Ok(num_bytes) => {
if num_bytes == 0 {
return Ok((crc_final(crc, size), size));
}
for &b in bytes[..num_bytes].iter() {
crc = crc_update(crc, b);
}
size += num_bytes;
}
Err(err) => return Err(err),
let num_bytes = rd.read(&mut bytes)?;
if num_bytes == 0 {
return Ok((crc_final(crc, size), size));
}
for &b in bytes[..num_bytes].iter() {
crc = crc_update(crc, b);
}
size += num_bytes;
}
}

View file

@ -50,9 +50,8 @@ fn mkdelim(col: usize, opts: &ArgMatches) -> String {
}
fn ensure_nl(line: &mut String) {
match line.chars().last() {
Some('\n') => (),
_ => line.push('\n'),
if !line.ends_with('\n') {
line.push('\n');
}
}

View file

@ -709,27 +709,26 @@ fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<S
return Err(format!("extra operand {:?}", paths[2]).into());
}
let (mut sources, target) = match options.target_dir {
let target = match options.target_dir {
Some(ref target) => {
// All path args are sources, and the target dir was
// specified separately
(paths, PathBuf::from(target))
PathBuf::from(target)
}
None => {
// If there was no explicit target-dir, then use the last
// path_arg
let target = paths.pop().unwrap();
(paths, target)
paths.pop().unwrap()
}
};
if options.strip_trailing_slashes {
for source in sources.iter_mut() {
for source in paths.iter_mut() {
*source = source.components().as_path().to_owned()
}
}
Ok((sources, target))
Ok((paths, target))
}
fn preserve_hardlinks(
@ -1088,7 +1087,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(()),
@ -1271,15 +1270,15 @@ fn copy_on_write_linux(source: &Path, dest: &Path, mode: ReflinkMode) -> CopyRes
ReflinkMode::Always => unsafe {
let result = ficlone(dst_file.as_raw_fd(), src_file.as_raw_fd() as *const i32);
if result != 0 {
return Err(format!(
Err(format!(
"failed to clone {:?} from {:?}: {}",
source,
dest,
std::io::Error::last_os_error()
)
.into());
.into())
} else {
return Ok(());
Ok(())
}
},
ReflinkMode::Auto => unsafe {
@ -1287,11 +1286,10 @@ fn copy_on_write_linux(source: &Path, dest: &Path, mode: ReflinkMode) -> CopyRes
if result != 0 {
fs::copy(source, dest).context(&*context_for(source, dest))?;
}
Ok(())
},
ReflinkMode::Never => unreachable!(),
}
Ok(())
}
/// Copies `source` to `dest` using copy-on-write if possible.

View file

@ -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

View file

@ -133,20 +133,12 @@ fn extract_patterns(args: &[String]) -> Result<Vec<Pattern>, CsplitError> {
Some(m) => m.as_str().parse().unwrap(),
};
if let Some(up_to_match) = captures.name("UPTO") {
let pattern = match Regex::new(up_to_match.as_str()) {
Err(_) => {
return Err(CsplitError::InvalidPattern(arg.to_string()));
}
Ok(reg) => reg,
};
let pattern = Regex::new(up_to_match.as_str())
.map_err(|_| CsplitError::InvalidPattern(arg.to_string()))?;
patterns.push(Pattern::UpToMatch(pattern, offset, execute_ntimes));
} else if let Some(skip_to_match) = captures.name("SKIPTO") {
let pattern = match Regex::new(skip_to_match.as_str()) {
Err(_) => {
return Err(CsplitError::InvalidPattern(arg.to_string()));
}
Ok(reg) => reg,
};
let pattern = Regex::new(skip_to_match.as_str())
.map_err(|_| CsplitError::InvalidPattern(arg.to_string()))?;
patterns.push(Pattern::SkipToMatch(pattern, offset, execute_ntimes));
}
} else if let Ok(line_number) = arg.parse::<usize>() {

View file

@ -33,13 +33,13 @@ impl SplitName {
// get the prefix
let prefix = prefix_opt.unwrap_or_else(|| "xx".to_string());
// the width for the split offset
let n_digits = match n_digits_opt {
None => 2,
Some(opt) => match opt.parse::<usize>() {
Ok(digits) => digits,
Err(_) => return Err(CsplitError::InvalidNumber(opt)),
},
};
let n_digits = n_digits_opt
.map(|opt| {
opt.parse::<usize>()
.map_err(|_| CsplitError::InvalidNumber(opt))
})
.transpose()?
.unwrap_or(2);
// translate the custom format into a function
let fn_split_name: Box<dyn Fn(usize) -> String> = match format_opt {
None => Box::new(move |n: usize| -> String {

View file

@ -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"

View file

@ -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>

View file

@ -19,6 +19,8 @@ clap = "2.33"
chrono = "0.4"
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version="0.3", features=[] }
[[bin]]

View file

@ -1,9 +1,9 @@
// This file is part of the uutils coreutils package.
//
// (c) Derek Chiang <derekchiang93@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// * This file is part of the uutils coreutils package.
// *
// * (c) Derek Chiang <derekchiang93@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
#[macro_use]
extern crate uucore;
@ -12,6 +12,7 @@ use chrono::prelude::DateTime;
use chrono::Local;
use clap::{crate_version, App, Arg};
use std::collections::HashSet;
use std::convert::TryFrom;
use std::env;
use std::fs;
#[cfg(not(windows))]
@ -24,8 +25,11 @@ 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::parse_size::{parse_size, ParseSizeError};
use uucore::InvalidEncodingHandling;
#[cfg(windows)]
use winapi::shared::minwindef::{DWORD, LPVOID};
@ -42,7 +46,7 @@ mod options {
pub const NULL: &str = "0";
pub const ALL: &str = "all";
pub const APPARENT_SIZE: &str = "apparent-size";
pub const BLOCK_SIZE: &str = "B";
pub const BLOCK_SIZE: &str = "block-size";
pub const BYTES: &str = "b";
pub const TOTAL: &str = "c";
pub const MAX_DEPTH: &str = "d";
@ -55,6 +59,7 @@ mod options {
pub const SI: &str = "si";
pub const TIME: &str = "time";
pub const TIME_STYLE: &str = "time-style";
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
pub const FILE: &str = "FILE";
}
@ -79,6 +84,7 @@ struct Options {
max_depth: Option<usize>,
total: bool,
separate_dirs: bool,
one_file_system: bool,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
@ -159,7 +165,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 +197,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) {
@ -223,64 +229,22 @@ fn get_file_info(path: &PathBuf) -> Option<FileInfo> {
result
}
fn unit_string_to_number(s: &str) -> Option<u64> {
let mut offset = 0;
let mut s_chars = s.chars().rev();
let (mut ch, multiple) = match s_chars.next() {
Some('B') | Some('b') => ('B', 1000u64),
Some(ch) => (ch, 1024u64),
None => return None,
};
if ch == 'B' {
ch = s_chars.next()?;
offset += 1;
}
ch = ch.to_ascii_uppercase();
let unit = UNITS
.iter()
.rev()
.find(|&&(unit_ch, _)| unit_ch == ch)
.map(|&(_, val)| {
// we found a match, so increment offset
offset += 1;
val
})
.or_else(|| if multiple == 1024 { Some(0) } else { None })?;
let number = s[..s.len() - offset].parse::<u64>().ok()?;
Some(number * multiple.pow(unit))
}
fn translate_to_pure_number(s: &Option<&str>) -> Option<u64> {
match *s {
Some(ref s) => unit_string_to_number(s),
None => None,
}
}
fn read_block_size(s: Option<&str>) -> u64 {
match translate_to_pure_number(&s) {
Some(v) => v,
None => {
if let Some(value) = s {
show_error!("invalid --block-size argument '{}'", value);
};
for env_var in &["DU_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"] {
let env_size = env::var(env_var).ok();
if let Some(quantity) = translate_to_pure_number(&env_size.as_deref()) {
return quantity;
fn read_block_size(s: Option<&str>) -> usize {
if let Some(s) = s {
parse_size(s)
.unwrap_or_else(|e| crash!(1, "{}", format_error_message(e, s, options::BLOCK_SIZE)))
} else {
for env_var in &["DU_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"] {
if let Ok(env_size) = env::var(env_var) {
if let Ok(v) = parse_size(&env_size) {
return v;
}
}
if env::var("POSIXLY_CORRECT").is_ok() {
512
} else {
1024
}
}
if env::var("POSIXLY_CORRECT").is_ok() {
512
} else {
1024
}
}
}
@ -316,10 +280,18 @@ fn du(
Ok(entry) => match Stat::new(entry.path()) {
Ok(this_stat) => {
if this_stat.is_dir {
if options.one_file_system {
if let (Some(this_inode), Some(my_inode)) =
(this_stat.inode, my_stat.inode)
{
if this_inode.dev_id != my_inode.dev_id {
continue;
}
}
}
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 +330,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,12 +405,12 @@ 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 testsuite uses this alias
.alias("app") // The GNU test suite uses this alias
)
.arg(
Arg::with_name(options::BLOCK_SIZE)
.short("B")
.long("block-size")
.long(options::BLOCK_SIZE)
.value_name("SIZE")
.help(
"scale sizes by SIZE before printing them. \
@ -530,12 +504,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.long(options::SI)
.help("like -h, but use powers of 1000 not 1024")
)
// .arg(
// Arg::with_name("one-file-system")
// .short("x")
// .long("one-file-system")
// .help("skip directories on different file systems")
// )
.arg(
Arg::with_name(options::ONE_FILE_SYSTEM)
.short("x")
.long(options::ONE_FILE_SYSTEM)
.help("skip directories on different file systems")
)
// .arg(
// Arg::with_name("")
// .short("x")
@ -583,12 +557,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 */ }
@ -600,6 +574,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
max_depth,
total: matches.is_present(options::TOTAL),
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
};
let files = match matches.value_of(options::FILE) {
@ -609,7 +584,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
};
let block_size = read_block_size(matches.value_of(options::BLOCK_SIZE));
let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap();
let multiplier: u64 = if matches.is_present(options::SI) {
1000
@ -745,31 +720,27 @@ Try '{} --help' for more information.",
0
}
fn format_error_message(error: ParseSizeError, s: &str, option: &str) -> String {
// NOTE:
// GNU's du echos affected flag, -B or --block-size (-t or --threshold), depending user's selection
// GNU's du does distinguish between "invalid (suffix in) argument"
match error {
ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s),
ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s),
}
}
#[cfg(test)]
mod test_du {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_translate_to_pure_number() {
let test_data = [
(Some("10".to_string()), Some(10)),
(Some("10K".to_string()), Some(10 * 1024)),
(Some("5M".to_string()), Some(5 * 1024 * 1024)),
(Some("900KB".to_string()), Some(900 * 1000)),
(Some("BAD_STRING".to_string()), None),
];
for it in test_data.iter() {
assert_eq!(translate_to_pure_number(&it.0.as_deref()), it.1);
}
}
#[test]
fn test_read_block_size() {
let test_data = [
(Some("10".to_string()), 10),
(Some("1024".to_string()), 1024),
(Some("K".to_string()), 1024),
(None, 1024),
(Some("BAD_STRING".to_string()), 1024),
];
for it in test_data.iter() {
assert_eq!(read_block_size(it.0.as_deref()), it.1);

View file

@ -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;
}

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

@ -82,13 +82,10 @@ fn load_config_file(opts: &mut Options) -> Result<(), i32> {
Ini::load_from_file(file)
};
let conf = match conf {
Ok(config) => config,
Err(error) => {
eprintln!("env: error: \"{}\": {}", file, error);
return Err(1);
}
};
let conf = conf.map_err(|error| {
eprintln!("env: error: \"{}\": {}", file, error);
1
})?;
for (_, prop) in &conf {
// ignore all INI section lines (treat them as comments)
@ -245,7 +242,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);
}
@ -256,13 +253,10 @@ fn run_env(args: impl uucore::Args) -> Result<(), i32> {
// FIXME: this should just use execvp() (no fork()) on Unix-like systems
match Command::new(&*prog).args(args).status() {
Ok(exit) => {
if !exit.success() {
return Err(exit.code().unwrap());
}
}
Ok(exit) if !exit.success() => return Err(exit.code().unwrap()),
Err(ref err) if err.kind() == io::ErrorKind::NotFound => return Err(127),
Err(_) => return Err(126),
Ok(_) => (),
}
} else {
// no program provided, so just dump all env vars to stdout

View file

@ -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]));
}

View file

@ -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 {

View file

@ -160,10 +160,8 @@ impl AstNode {
if let AstNode::Node { operands, .. } = self {
let mut out = Vec::with_capacity(operands.len());
for operand in operands {
match operand.evaluate() {
Ok(value) => out.push(value),
Err(reason) => return Err(reason),
}
let value = operand.evaluate()?;
out.push(value);
}
Ok(out)
} else {
@ -175,23 +173,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 +194,7 @@ pub fn tokens_to_ast(
maybe_dump_ast(&result);
result
}
}
})
}
fn maybe_dump_ast(result: &Result<Box<AstNode>, String>) {
@ -261,10 +250,8 @@ fn maybe_ast_node(
) -> Result<Box<AstNode>, String> {
let mut operands = Vec::with_capacity(arity);
for _ in 0..arity {
match ast_from_rpn(rpn) {
Err(reason) => return Err(reason),
Ok(operand) => operands.push(operand),
}
let operand = ast_from_rpn(rpn)?;
operands.push(operand);
}
operands.reverse();
Ok(AstNode::new_node(token_idx, op_type, operands))
@ -408,10 +395,12 @@ fn move_till_match_paren(
op_stack: &mut TokenStack,
) -> Result<(), String> {
loop {
match op_stack.pop() {
None => return Err("syntax error (Mismatched close-parenthesis)".to_string()),
Some((_, Token::ParOpen)) => return Ok(()),
Some(other) => out_stack.push(other),
let op = op_stack
.pop()
.ok_or_else(|| "syntax error (Mismatched close-parenthesis)".to_string())?;
match op {
(_, Token::ParOpen) => return Ok(()),
other => out_stack.push(other),
}
}
}
@ -471,22 +460,17 @@ fn infix_operator_and(values: &[String]) -> String {
fn operator_match(values: &[String]) -> Result<String, String> {
assert!(values.len() == 2);
let re = match Regex::with_options(&values[1], RegexOptions::REGEX_OPTION_NONE, Syntax::grep())
{
Ok(m) => m,
Err(err) => return Err(err.description().to_string()),
};
if re.captures_len() > 0 {
Ok(match re.captures(&values[0]) {
Some(captures) => captures.at(1).unwrap().to_string(),
None => "".to_string(),
})
let re = Regex::with_options(&values[1], RegexOptions::REGEX_OPTION_NONE, Syntax::grep())
.map_err(|err| err.description().to_string())?;
Ok(if re.captures_len() > 0 {
re.captures(&values[0])
.map(|captures| captures.at(1).unwrap())
.unwrap_or("")
.to_string()
} else {
Ok(match re.find(&values[0]) {
Some((start, end)) => (end - start).to_string(),
None => "0".to_string(),
})
}
re.find(&values[0])
.map_or("0".to_string(), |(start, end)| (end - start).to_string())
})
}
fn prefix_operator_length(values: &[String]) -> String {

View file

@ -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);

View file

@ -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 == "-" {

View file

@ -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"

View file

@ -59,7 +59,7 @@ impl Digest for blake2b_simd::State {
fn result(&mut self, out: &mut [u8]) {
let hash_result = &self.finalize();
out.copy_from_slice(&hash_result.as_bytes());
out.copy_from_slice(hash_result.as_bytes());
}
fn reset(&mut self) {

View file

@ -90,7 +90,7 @@ fn detect_algo<'a>(
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>,
@ -140,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>,
@ -151,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>,
@ -194,7 +194,7 @@ fn detect_algo<'a>(
}
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>,
@ -238,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),
},
@ -247,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),
},
@ -255,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)
}
}
}

View file

@ -1,3 +1,8 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore (vars) zlines
use clap::{crate_version, App, Arg};
@ -75,7 +80,7 @@ fn app<'a>() -> App<'a, 'a> {
.arg(
Arg::with_name(options::QUIET_NAME)
.short("q")
.long("--quiet")
.long("quiet")
.visible_alias("silent")
.help("never print headers giving file names")
.overrides_with_all(&[options::VERBOSE_NAME, options::QUIET_NAME]),
@ -108,12 +113,7 @@ where
{
match parse::parse_num(src) {
Ok((n, last)) => Ok((closure(n), last)),
Err(reason) => match reason {
parse::ParseError::Syntax => Err(format!("'{}'", src)),
parse::ParseError::Overflow => {
Err(format!("'{}': Value too large for defined datatype", src))
}
},
Err(e) => Err(e.to_string()),
}
}
@ -176,19 +176,11 @@ impl HeadOptions {
options.zeroed = matches.is_present(options::ZERO_NAME);
let mode_and_from_end = if let Some(v) = matches.value_of(options::BYTES_NAME) {
match parse_mode(v, Modes::Bytes) {
Ok(v) => v,
Err(err) => {
return Err(format!("invalid number of bytes: {}", err));
}
}
parse_mode(v, Modes::Bytes)
.map_err(|err| format!("invalid number of bytes: {}", err))?
} else if let Some(v) = matches.value_of(options::LINES_NAME) {
match parse_mode(v, Modes::Lines) {
Ok(v) => v,
Err(err) => {
return Err(format!("invalid number of lines: {}", err));
}
}
parse_mode(v, Modes::Lines)
.map_err(|err| format!("invalid number of lines: {}", err))?
} else {
(Modes::Lines(10), false)
};
@ -474,7 +466,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let args = match HeadOptions::get_from(args) {
Ok(o) => o,
Err(s) => {
crash!(EXIT_FAILURE, "head: {}", s);
crash!(EXIT_FAILURE, "{}", s);
}
};
match uu_head(&args) {

View file

@ -1,5 +1,10 @@
use std::convert::TryFrom;
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
use std::ffi::OsString;
use uucore::parse_size::{parse_size, ParseSizeError};
#[derive(PartialEq, Debug)]
pub enum ParseError {
@ -92,92 +97,25 @@ pub fn parse_obsolete(src: &str) -> Option<Result<impl Iterator<Item = OsString>
}
/// Parses an -c or -n argument,
/// the bool specifies whether to read from the end
pub fn parse_num(src: &str) -> Result<(usize, bool), ParseError> {
let mut num_start = 0;
let mut chars = src.char_indices();
let (mut chars, all_but_last) = match chars.next() {
Some((_, c)) => {
pub fn parse_num(src: &str) -> Result<(usize, bool), ParseSizeError> {
let mut size_string = src.trim();
let mut all_but_last = false;
if let Some(c) = size_string.chars().next() {
if c == '+' || c == '-' {
// head: '+' is not documented (8.32 man pages)
size_string = &size_string[1..];
if c == '-' {
num_start += 1;
(chars, true)
} else {
(src.char_indices(), false)
all_but_last = true;
}
}
None => return Err(ParseError::Syntax),
};
let mut num_end = 0usize;
let mut last_char = 0 as char;
let mut num_count = 0usize;
for (n, c) in &mut chars {
if c.is_numeric() {
num_end = n;
num_count += 1;
} else {
last_char = c;
break;
}
} else {
return Err(ParseSizeError::ParseFailure(src.to_string()));
}
let num = if num_count > 0 {
match src[num_start..=num_end].parse::<usize>() {
Ok(n) => Some(n),
Err(_) => return Err(ParseError::Overflow),
}
} else {
None
};
if last_char == 0 as char {
if let Some(n) = num {
Ok((n, all_but_last))
} else {
Err(ParseError::Syntax)
}
} else {
let base: u128 = match chars.next() {
Some((_, c)) => {
let b = match c {
'B' if last_char != 'b' => 1000,
'i' if last_char != 'b' => {
if let Some((_, 'B')) = chars.next() {
1024
} else {
return Err(ParseError::Syntax);
}
}
_ => return Err(ParseError::Syntax),
};
if chars.next().is_some() {
return Err(ParseError::Syntax);
} else {
b
}
}
None => 1024,
};
let mul = match last_char.to_lowercase().next().unwrap() {
'b' => 512,
'k' => base.pow(1),
'm' => base.pow(2),
'g' => base.pow(3),
't' => base.pow(4),
'p' => base.pow(5),
'e' => base.pow(6),
'z' => base.pow(7),
'y' => base.pow(8),
_ => return Err(ParseError::Syntax),
};
let mul = match usize::try_from(mul) {
Ok(n) => n,
Err(_) => return Err(ParseError::Overflow),
};
match num.unwrap_or(1).checked_mul(mul) {
Some(n) => Ok((n, all_but_last)),
None => Err(ParseError::Overflow),
}
}
parse_size(size_string).map(|n| (n, all_but_last))
}
#[cfg(test)]
mod tests {
use super::*;
@ -195,44 +133,6 @@ mod tests {
Some(Ok(src.iter().map(|s| s.to_string()).collect()))
}
#[test]
#[cfg(not(target_pointer_width = "128"))]
fn test_parse_overflow_x64() {
assert_eq!(parse_num("1Y"), Err(ParseError::Overflow));
assert_eq!(parse_num("1Z"), Err(ParseError::Overflow));
assert_eq!(parse_num("100E"), Err(ParseError::Overflow));
assert_eq!(parse_num("100000P"), Err(ParseError::Overflow));
assert_eq!(parse_num("1000000000T"), Err(ParseError::Overflow));
assert_eq!(
parse_num("10000000000000000000000"),
Err(ParseError::Overflow)
);
}
#[test]
#[cfg(target_pointer_width = "32")]
fn test_parse_overflow_x32() {
assert_eq!(parse_num("1T"), Err(ParseError::Overflow));
assert_eq!(parse_num("1000G"), Err(ParseError::Overflow));
}
#[test]
fn test_parse_bad_syntax() {
assert_eq!(parse_num("5MiB nonsense"), Err(ParseError::Syntax));
assert_eq!(parse_num("Nonsense string"), Err(ParseError::Syntax));
assert_eq!(parse_num("5mib"), Err(ParseError::Syntax));
assert_eq!(parse_num("biB"), Err(ParseError::Syntax));
assert_eq!(parse_num("-"), Err(ParseError::Syntax));
assert_eq!(parse_num(""), Err(ParseError::Syntax));
}
#[test]
fn test_parse_numbers() {
assert_eq!(parse_num("k"), Ok((1024, false)));
assert_eq!(parse_num("MiB"), Ok((1024 * 1024, false)));
assert_eq!(parse_num("-5"), Ok((5, true)));
assert_eq!(parse_num("b"), Ok((512, false)));
assert_eq!(parse_num("-2GiB"), Ok((2 * 1024 * 1024 * 1024, true)));
assert_eq!(parse_num("5M"), Ok((5 * 1024 * 1024, false)));
assert_eq!(parse_num("5MB"), Ok((5 * 1000 * 1000, false)));
}
#[test]
fn test_parse_numbers_obsolete() {
assert_eq!(obsolete("-5"), obsolete_result(&["-n", "5"]));
assert_eq!(obsolete("-100"), obsolete_result(&["-n", "100"]));

View file

@ -299,29 +299,17 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
let considering_dir: bool = MainFunction::Directory == main_function;
let specified_mode: Option<u32> = if matches.is_present(OPT_MODE) {
match matches.value_of(OPT_MODE) {
Some(x) => match mode::parse(x, considering_dir) {
Ok(y) => Some(y),
Err(err) => {
show_error!("Invalid mode string: {}", err);
return Err(1);
}
},
None => {
return Err(1);
}
}
let x = matches.value_of(OPT_MODE).ok_or(1)?;
Some(mode::parse(x, considering_dir).map_err(|err| {
show_error!("Invalid mode string: {}", err);
1
})?)
} else {
None
};
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
match matches.value_of(OPT_SUFFIX) {
Some(x) => x,
None => {
return Err(1);
}
}
matches.value_of(OPT_SUFFIX).ok_or(1)?
} else {
"~"
};
@ -379,7 +367,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 +410,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 +489,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 +551,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(());
}

View file

@ -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,

View file

@ -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) => {

View file

@ -27,7 +27,6 @@ use uucore::fs::{canonicalize, CanonicalizeMode};
pub struct Settings {
overwrite: OverwriteMode,
backup: BackupMode,
force: bool,
suffix: String,
symbolic: bool,
relative: bool,
@ -54,7 +53,7 @@ pub enum BackupMode {
fn get_usage() -> String {
format!(
"{0} [OPTION]... [-T] TARGET LINK_executable!() (1st form)
"{0} [OPTION]... [-T] TARGET LINK_NAME (1st form)
{0} [OPTION]... TARGET (2nd form)
{0} [OPTION]... TARGET... DIRECTORY (3rd form)
{0} [OPTION]... -t DIRECTORY TARGET... (4th form)",
@ -64,7 +63,7 @@ fn get_usage() -> String {
fn get_long_usage() -> String {
String::from(
" In the 1st form, create a link to TARGET with the name LINK_executable!().
" In the 1st form, create a link to TARGET with the name LINK_NAME.
In the 2nd form, create a link to TARGET in the current directory.
In the 3rd and 4th forms, create links to each TARGET in DIRECTORY.
Create hard links by default, symbolic links with --symbolic.
@ -78,17 +77,19 @@ fn get_long_usage() -> String {
static ABOUT: &str = "change file owner and group";
static OPT_B: &str = "b";
static OPT_BACKUP: &str = "backup";
static OPT_FORCE: &str = "force";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_NO_DEREFERENCE: &str = "no-dereference";
static OPT_SYMBOLIC: &str = "symbolic";
static OPT_SUFFIX: &str = "suffix";
static OPT_TARGET_DIRECTORY: &str = "target-directory";
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
static OPT_RELATIVE: &str = "relative";
static OPT_VERBOSE: &str = "verbose";
mod options {
pub const B: &str = "b";
pub const BACKUP: &str = "backup";
pub const FORCE: &str = "force";
pub const INTERACTIVE: &str = "interactive";
pub const NO_DEREFERENCE: &str = "no-dereference";
pub const SYMBOLIC: &str = "symbolic";
pub const SUFFIX: &str = "suffix";
pub const TARGET_DIRECTORY: &str = "target-directory";
pub const NO_TARGET_DIRECTORY: &str = "no-target-directory";
pub const RELATIVE: &str = "relative";
pub const VERBOSE: &str = "verbose";
}
static ARG_FILES: &str = "files";
@ -101,49 +102,44 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.about(ABOUT)
.usage(&usage[..])
.after_help(&long_usage[..])
.arg(Arg::with_name(OPT_B).short(OPT_B).help(
.arg(Arg::with_name(options::B).short(options::B).help(
"make a backup of each file that would otherwise be overwritten or \
removed",
))
.arg(
Arg::with_name(OPT_BACKUP)
.long(OPT_BACKUP)
Arg::with_name(options::BACKUP)
.long(options::BACKUP)
.help(
"make a backup of each file that would otherwise be overwritten \
or removed",
)
.takes_value(true)
.possible_value("simple")
.possible_value("never")
.possible_value("numbered")
.possible_value("t")
.possible_value("existing")
.possible_value("nil")
.possible_value("none")
.possible_value("off")
.possible_values(&[
"simple", "never", "numbered", "t", "existing", "nil", "none", "off",
])
.value_name("METHOD"),
)
// TODO: opts.arg(
// Arg::with_name(("d", "directory", "allow users with appropriate privileges to attempt \
// to make hard links to directories");
.arg(
Arg::with_name(OPT_FORCE)
Arg::with_name(options::FORCE)
.short("f")
.long(OPT_FORCE)
.long(options::FORCE)
.help("remove existing destination files"),
)
.arg(
Arg::with_name(OPT_INTERACTIVE)
Arg::with_name(options::INTERACTIVE)
.short("i")
.long(OPT_INTERACTIVE)
.long(options::INTERACTIVE)
.help("prompt whether to remove existing destination files"),
)
.arg(
Arg::with_name(OPT_NO_DEREFERENCE)
Arg::with_name(options::NO_DEREFERENCE)
.short("n")
.long(OPT_NO_DEREFERENCE)
.long(options::NO_DEREFERENCE)
.help(
"treat LINK_executable!() as a normal file if it is a \
"treat LINK_NAME as a normal file if it is a \
symbolic link to a directory",
),
)
@ -153,43 +149,46 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
// TODO: opts.arg(
// Arg::with_name(("P", "physical", "make hard links directly to symbolic links");
.arg(
Arg::with_name(OPT_SYMBOLIC)
Arg::with_name(options::SYMBOLIC)
.short("s")
.long("symbolic")
.help("make symbolic links instead of hard links"),
.help("make symbolic links instead of hard links")
// override added for https://github.com/uutils/coreutils/issues/2359
.overrides_with(options::SYMBOLIC),
)
.arg(
Arg::with_name(OPT_SUFFIX)
Arg::with_name(options::SUFFIX)
.short("S")
.long(OPT_SUFFIX)
.long(options::SUFFIX)
.help("override the usual backup suffix")
.value_name("SUFFIX")
.takes_value(true),
)
.arg(
Arg::with_name(OPT_TARGET_DIRECTORY)
Arg::with_name(options::TARGET_DIRECTORY)
.short("t")
.long(OPT_TARGET_DIRECTORY)
.long(options::TARGET_DIRECTORY)
.help("specify the DIRECTORY in which to create the links")
.value_name("DIRECTORY")
.conflicts_with(OPT_NO_TARGET_DIRECTORY),
.conflicts_with(options::NO_TARGET_DIRECTORY),
)
.arg(
Arg::with_name(OPT_NO_TARGET_DIRECTORY)
Arg::with_name(options::NO_TARGET_DIRECTORY)
.short("T")
.long(OPT_NO_TARGET_DIRECTORY)
.help("treat LINK_executable!() as a normal file always"),
.long(options::NO_TARGET_DIRECTORY)
.help("treat LINK_NAME as a normal file always"),
)
.arg(
Arg::with_name(OPT_RELATIVE)
Arg::with_name(options::RELATIVE)
.short("r")
.long(OPT_RELATIVE)
.help("create symbolic links relative to link location"),
.long(options::RELATIVE)
.help("create symbolic links relative to link location")
.requires(options::SYMBOLIC),
)
.arg(
Arg::with_name(OPT_VERBOSE)
Arg::with_name(options::VERBOSE)
.short("v")
.long(OPT_VERBOSE)
.long(options::VERBOSE)
.help("print name of each linked file"),
)
.arg(
@ -209,18 +208,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.map(PathBuf::from)
.collect();
let overwrite_mode = if matches.is_present(OPT_FORCE) {
let overwrite_mode = if matches.is_present(options::FORCE) {
OverwriteMode::Force
} else if matches.is_present(OPT_INTERACTIVE) {
} else if matches.is_present(options::INTERACTIVE) {
OverwriteMode::Interactive
} else {
OverwriteMode::NoClobber
};
let backup_mode = if matches.is_present(OPT_B) {
let backup_mode = if matches.is_present(options::B) {
BackupMode::ExistingBackup
} else if matches.is_present(OPT_BACKUP) {
match matches.value_of(OPT_BACKUP) {
} else if matches.is_present(options::BACKUP) {
match matches.value_of(options::BACKUP) {
None => BackupMode::ExistingBackup,
Some(mode) => match mode {
"simple" | "never" => BackupMode::SimpleBackup,
@ -234,8 +233,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
BackupMode::NoBackup
};
let backup_suffix = if matches.is_present(OPT_SUFFIX) {
matches.value_of(OPT_SUFFIX).unwrap()
let backup_suffix = if matches.is_present(options::SUFFIX) {
matches.value_of(options::SUFFIX).unwrap()
} else {
"~"
};
@ -243,14 +242,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let settings = Settings {
overwrite: overwrite_mode,
backup: backup_mode,
force: matches.is_present(OPT_FORCE),
suffix: backup_suffix.to_string(),
symbolic: matches.is_present(OPT_SYMBOLIC),
relative: matches.is_present(OPT_RELATIVE),
target_dir: matches.value_of(OPT_TARGET_DIRECTORY).map(String::from),
no_target_dir: matches.is_present(OPT_NO_TARGET_DIRECTORY),
no_dereference: matches.is_present(OPT_NO_DEREFERENCE),
verbose: matches.is_present(OPT_VERBOSE),
symbolic: matches.is_present(options::SYMBOLIC),
relative: matches.is_present(options::RELATIVE),
target_dir: matches
.value_of(options::TARGET_DIRECTORY)
.map(String::from),
no_target_dir: matches.is_present(options::NO_TARGET_DIRECTORY),
no_dereference: matches.is_present(options::NO_DEREFERENCE),
verbose: matches.is_present(options::VERBOSE),
};
exec(&paths[..], &settings)
@ -260,17 +260,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);
}
}
@ -310,47 +310,48 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
let mut all_successful = true;
for srcpath in files.iter() {
let targetpath = if settings.no_dereference && settings.force {
// In that case, we don't want to do link resolution
// We need to clean the target
if is_symlink(target_dir) {
if target_dir.is_file() {
if let Err(e) = fs::remove_file(target_dir) {
show_error!("Could not update {}: {}", target_dir.display(), e)
};
}
if target_dir.is_dir() {
// Not sure why but on Windows, the symlink can be
// considered as a dir
// See test_ln::test_symlink_no_deref_dir
if let Err(e) = fs::remove_dir(target_dir) {
show_error!("Could not update {}: {}", target_dir.display(), e)
};
}
}
target_dir.to_path_buf()
} else {
match srcpath.as_os_str().to_str() {
Some(name) => {
match Path::new(name).file_name() {
Some(basename) => target_dir.join(basename),
// This can be None only for "." or "..". Trying
// to create a link with such name will fail with
// EEXIST, which agrees with the behavior of GNU
// coreutils.
None => target_dir.join(name),
let targetpath =
if settings.no_dereference && matches!(settings.overwrite, OverwriteMode::Force) {
// In that case, we don't want to do link resolution
// We need to clean the target
if is_symlink(target_dir) {
if target_dir.is_file() {
if let Err(e) = fs::remove_file(target_dir) {
show_error!("Could not update {}: {}", target_dir.display(), e)
};
}
if target_dir.is_dir() {
// Not sure why but on Windows, the symlink can be
// considered as a dir
// See test_ln::test_symlink_no_deref_dir
if let Err(e) = fs::remove_dir(target_dir) {
show_error!("Could not update {}: {}", target_dir.display(), e)
};
}
}
None => {
show_error!(
"cannot stat '{}': No such file or directory",
srcpath.display()
);
all_successful = false;
continue;
target_dir.to_path_buf()
} else {
match srcpath.as_os_str().to_str() {
Some(name) => {
match Path::new(name).file_name() {
Some(basename) => target_dir.join(basename),
// This can be None only for "." or "..". Trying
// to create a link with such name will fail with
// EEXIST, which agrees with the behavior of GNU
// coreutils.
None => target_dir.join(name),
}
}
None => {
show_error!(
"cannot stat '{}': No such file or directory",
srcpath.display()
);
all_successful = false;
continue;
}
}
}
};
};
if let Err(e) = link(srcpath, &targetpath, settings) {
show_error!(
@ -371,7 +372,8 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings)
fn relative_path<'a>(src: &Path, dst: &Path) -> Result<Cow<'a, Path>> {
let src_abs = canonicalize(src, CanonicalizeMode::Normal)?;
let dst_abs = canonicalize(dst, CanonicalizeMode::Normal)?;
let mut dst_abs = canonicalize(dst.parent().unwrap(), CanonicalizeMode::Normal)?;
dst_abs.push(dst.components().last().unwrap());
let suffix_pos = src_abs
.components()
.zip(dst_abs.components())
@ -392,7 +394,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()
};
@ -421,10 +423,6 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> {
}
}
if settings.no_dereference && settings.force && dst.exists() {
fs::remove_file(dst)?;
}
if settings.symbolic {
symlink(&source, dst)?;
} else {

View file

@ -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,
}

View file

@ -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"),

View file

@ -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);

View file

@ -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)
}
}

View file

@ -19,7 +19,7 @@ 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"

View file

@ -11,7 +11,6 @@
extern crate uucore;
use std::{
convert::TryInto,
fs::File,
io::{stdin, stdout, BufReader, Read, Stdout, Write},
path::Path,
@ -32,6 +31,8 @@ use crossterm::{
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
const BELL: &str = "\x07";
pub mod options {
pub const SILENT: &str = "silent";
pub const LOGICAL: &str = "logical";
@ -53,14 +54,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let matches = App::new(executable!())
.about("A file perusal filter for CRT viewing.")
.version(crate_version!())
// The commented arguments below are unimplemented:
/*
.arg(
Arg::with_name(options::SILENT)
.short("d")
.long(options::SILENT)
.help("Display help instead of ringing bell"),
)
// The commented arguments below are unimplemented:
/*
.arg(
Arg::with_name(options::LOGICAL)
.short("f")
@ -140,6 +141,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.get_matches_from(args);
let mut buff = String::new();
let silent = matches.is_present(options::SILENT);
if let Some(files) = matches.values_of(options::FILES) {
let mut stdout = setup_term();
let length = files.len();
@ -162,14 +164,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();
more(&buff, &mut stdout, next_file.copied());
more(&buff, &mut stdout, next_file.copied(), silent);
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, None);
more(&buff, &mut stdout, None, silent);
reset_term(&mut stdout);
} else {
show_usage_error!("bad usage");
@ -204,38 +206,18 @@ fn reset_term(stdout: &mut std::io::Stdout) {
#[inline(always)]
fn reset_term(_: &mut usize) {}
fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
let (cols, rows) = terminal::size().unwrap();
let lines = break_buff(buff, usize::from(cols));
let line_count: u16 = lines.len().try_into().unwrap();
let mut upper_mark = 0;
let mut lines_left = line_count.saturating_sub(upper_mark + rows);
draw(
&mut upper_mark,
rows,
&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.
let mut to_be_done = false;
if lines_left == 0 && is_last {
if is_last {
return;
} else {
to_be_done = true;
}
let mut pager = Pager::new(rows as usize, lines, next_file, silent);
pager.draw(stdout, false);
if pager.should_close() {
return;
}
loop {
let mut wrong_key = false;
if event::poll(Duration::from_millis(10)).unwrap() {
match event::read().unwrap() {
Event::Key(KeyEvent {
@ -257,59 +239,127 @@ fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>) {
code: KeyCode::Char(' '),
modifiers: KeyModifiers::NONE,
}) => {
upper_mark = upper_mark.saturating_add(rows.saturating_sub(1));
pager.page_down();
}
Event::Key(KeyEvent {
code: KeyCode::Up,
modifiers: KeyModifiers::NONE,
}) => {
upper_mark = upper_mark.saturating_sub(rows.saturating_sub(1));
pager.page_up();
}
_ => {
wrong_key = true;
}
_ => continue,
}
lines_left = line_count.saturating_sub(upper_mark + rows);
draw(
&mut upper_mark,
rows,
&mut stdout,
lines.clone(),
line_count,
next_file,
);
if lines_left == 0 {
if to_be_done || is_last {
return;
}
to_be_done = true;
pager.draw(stdout, wrong_key);
if pager.should_close() {
return;
}
}
}
}
fn draw(
upper_mark: &mut u16,
rows: u16,
mut stdout: &mut std::io::Stdout,
struct Pager<'a> {
// The current line at the top of the screen
upper_mark: usize,
// The number of rows that fit on the screen
content_rows: usize,
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);
// Reduce the row by 1 for the prompt
let displayed_lines = lines
.iter()
.skip(up_mark.into())
.take(usize::from(rows.saturating_sub(1)));
next_file: Option<&'a str>,
line_count: usize,
close_on_down: bool,
silent: bool,
}
for line in displayed_lines {
stdout
.write_all(format!("\r{}\n", line).as_bytes())
.unwrap();
impl<'a> Pager<'a> {
fn new(rows: usize, lines: Vec<String>, next_file: Option<&'a str>, silent: bool) -> Self {
let line_count = lines.len();
Self {
upper_mark: 0,
content_rows: rows - 1,
lines,
next_file,
line_count,
close_on_down: false,
silent,
}
}
fn should_close(&mut self) -> bool {
if self.upper_mark + self.content_rows >= self.line_count {
if self.close_on_down {
return true;
}
if self.next_file.is_none() {
return true;
} else {
self.close_on_down = true;
}
} else {
self.close_on_down = false;
}
false
}
fn page_down(&mut self) {
self.upper_mark += self.content_rows;
}
fn page_up(&mut self) {
self.upper_mark = self.upper_mark.saturating_sub(self.content_rows);
}
fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) {
let lower_mark = self.line_count.min(self.upper_mark + self.content_rows);
self.draw_lines(stdout);
self.draw_prompt(stdout, lower_mark, wrong_key);
stdout.flush().unwrap();
}
fn draw_lines(&self, stdout: &mut std::io::Stdout) {
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
let displayed_lines = self
.lines
.iter()
.skip(self.upper_mark)
.take(self.content_rows);
for line in displayed_lines {
stdout
.write_all(format!("\r{}\n", line).as_bytes())
.unwrap();
}
}
fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: bool) {
let status_inner = if lower_mark == self.line_count {
format!("Next file: {}", self.next_file.unwrap_or_default())
} else {
format!(
"{}%",
(lower_mark as f64 / self.line_count as f64 * 100.0).round() as u16
)
};
let status = format!("--More--({})", status_inner);
let banner = match (self.silent, wrong_key) {
(true, true) => "[Press 'h' for instructions. (unimplemented)]".to_string(),
(true, false) => format!("{}[Press space to continue, 'q' to quit.]", status),
(false, true) => format!("{}{}", status, BELL),
(false, false) => status,
};
write!(
stdout,
"\r{}{}{}",
Attribute::Reverse,
banner,
Attribute::Reset
)
.unwrap();
}
make_prompt_and_flush(&mut stdout, lower_mark, lc, next_file);
*upper_mark = up_mark;
}
// Break the lines on the cols of the terminal
@ -350,52 +400,11 @@ fn break_line(line: &str, cols: usize) -> Vec<String> {
lines
}
// Calculate upper_mark based on certain parameters
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).saturating_add(1);
lower_mark = line_count;
} else {
lower_mark = lower_mark.saturating_sub(1)
}
(upper_mark, lower_mark)
}
// Make a prompt similar to original more
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--({}){}",
Attribute::Reverse,
status,
Attribute::Reset
)
.unwrap();
stdout.flush().unwrap();
}
#[cfg(test)]
mod tests {
use super::{break_line, calc_range};
use super::break_line;
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!((76, 100), calc_range(85, 25, 100));
}
#[test]
fn test_break_lines_long() {
let mut test_string = String::with_capacity(100);

View file

@ -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

View file

@ -247,7 +247,7 @@ fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
let mut line_filter: fn(&str, &regex::Regex) -> bool = pass_regex;
for mut l in reader.lines().map(|r| r.unwrap()) {
// Sanitize the string. We want to print the newline ourselves.
if !l.is_empty() && l.chars().rev().next().unwrap() == '\n' {
if l.ends_with('\n') {
l.pop();
}
// Next we iterate through the individual chars to see if this

View file

@ -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" }

View file

@ -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())
}
}

View file

@ -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);

View file

@ -2,6 +2,7 @@
use std::fmt;
#[allow(clippy::enum_variant_names)]
#[derive(Copy)]
pub enum FormatWriter {
IntWriter(fn(u64) -> String),

View file

@ -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);
}

View file

@ -43,6 +43,7 @@ use crate::partialreader::*;
use crate::peekreader::*;
use crate::prn_char::format_ascii_dump;
use clap::{self, crate_version, AppSettings, Arg, ArgMatches};
use uucore::parse_size::ParseSizeError;
use uucore::InvalidEncodingHandling;
const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes
@ -128,42 +129,34 @@ impl OdOptions {
}
};
let mut skip_bytes = match matches.value_of(options::SKIP_BYTES) {
None => 0,
Some(s) => match parse_number_of_bytes(&s) {
Ok(i) => i,
Err(_) => {
return Err(format!("Invalid argument --skip-bytes={}", s));
}
},
};
let mut skip_bytes = matches.value_of(options::SKIP_BYTES).map_or(0, |s| {
parse_number_of_bytes(s).unwrap_or_else(|e| {
crash!(1, "{}", format_error_message(e, s, options::SKIP_BYTES))
})
});
let mut label: Option<usize> = None;
let input_strings = match parse_inputs(&matches) {
Ok(CommandLineInputs::FileNames(v)) => v,
Ok(CommandLineInputs::FileAndOffset((f, s, l))) => {
let parsed_input = parse_inputs(&matches).map_err(|e| format!("Invalid inputs: {}", e))?;
let input_strings = match parsed_input {
CommandLineInputs::FileNames(v) => v,
CommandLineInputs::FileAndOffset((f, s, l)) => {
skip_bytes = s;
label = l;
vec![f]
}
Err(e) => {
return Err(format!("Invalid inputs: {}", e));
}
};
let formats = match parse_format_flags(&args) {
Ok(f) => f,
Err(e) => {
return Err(e);
}
};
let formats = parse_format_flags(&args)?;
let mut line_bytes = matches.value_of(options::WIDTH).map_or(16, |s| {
if matches.occurrences_of(options::WIDTH) == 0 {
return 16;
};
parse_number_of_bytes(s)
.unwrap_or_else(|e| crash!(1, "{}", format_error_message(e, s, options::WIDTH)))
});
let mut line_bytes = match matches.value_of(options::WIDTH) {
None => 16,
Some(_) if matches.occurrences_of(options::WIDTH) == 0 => 16,
Some(s) => s.parse::<usize>().unwrap_or(0),
};
let min_bytes = formats.iter().fold(1, |max, next| {
cmp::max(max, next.formatter_item_info.byte_size)
});
@ -174,15 +167,11 @@ impl OdOptions {
let output_duplicates = matches.is_present(options::OUTPUT_DUPLICATES);
let read_bytes = match matches.value_of(options::READ_BYTES) {
None => None,
Some(s) => match parse_number_of_bytes(&s) {
Ok(i) => Some(i),
Err(_) => {
return Err(format!("Invalid argument --read-bytes={}", s));
}
},
};
let read_bytes = matches.value_of(options::READ_BYTES).map(|s| {
parse_number_of_bytes(s).unwrap_or_else(|e| {
crash!(1, "{}", format_error_message(e, s, options::READ_BYTES))
})
});
let radix = match matches.value_of(options::ADDRESS_RADIX) {
None => Radix::Octal,
@ -263,7 +252,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.short("S")
.long(options::STRINGS)
.help(
"output strings of at least BYTES graphic chars. 3 is assumed when \
"NotImplemented: output strings of at least BYTES graphic chars. 3 is assumed when \
BYTES is not specified.",
)
.default_value("3")
@ -453,8 +442,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let od_options = match OdOptions::new(clap_matches, args) {
Err(s) => {
show_usage_error!("{}", s);
return 1;
crash!(1, "{}", s);
}
Ok(o) => o,
};
@ -537,7 +525,7 @@ where
print_bytes(
&input_offset.format_byte_offset(),
&memory_decoder,
&output_info,
output_info,
);
}
@ -636,3 +624,13 @@ fn open_input_peek_reader(
let pr = PartialReader::new(mf, skip_bytes, read_bytes);
PeekReader::new(pr)
}
fn format_error_message(error: ParseSizeError, s: &str, option: &str) -> String {
// NOTE:
// GNU's od echos affected flag, -N or --read-bytes (-j or --skip-bytes, etc.), depending user's selection
// GNU's od does distinguish between "invalid (suffix in) argument"
match error {
ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s),
ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s),
}
}

View file

@ -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,

View file

@ -108,10 +108,8 @@ pub fn parse_format_flags(args: &[String]) -> Result<Vec<ParsedFormatterItemInfo
for arg in arg_iter {
if expect_type_string {
match parse_type_string(arg) {
Ok(v) => formats.extend(v.into_iter()),
Err(e) => return Err(e),
}
let v = parse_type_string(arg)?;
formats.extend(v.into_iter());
expect_type_string = false;
} else if arg.starts_with("--") {
if arg.len() == 2 {
@ -119,10 +117,8 @@ pub fn parse_format_flags(args: &[String]) -> Result<Vec<ParsedFormatterItemInfo
}
if arg.starts_with("--format=") {
let params: String = arg.chars().skip_while(|c| *c != '=').skip(1).collect();
match parse_type_string(&params) {
Ok(v) => formats.extend(v.into_iter()),
Err(e) => return Err(e),
}
let v = parse_type_string(&params)?;
formats.extend(v.into_iter());
}
if arg == "--format" {
expect_type_string = true;
@ -145,10 +141,8 @@ pub fn parse_format_flags(args: &[String]) -> Result<Vec<ParsedFormatterItemInfo
}
}
if !format_spec.is_empty() {
match parse_type_string(&format_spec) {
Ok(v) => formats.extend(v.into_iter()),
Err(e) => return Err(e),
}
let v = parse_type_string(&format_spec)?;
formats.extend(v.into_iter());
expect_type_string = false;
}
}
@ -275,17 +269,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);
@ -301,30 +291,25 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
ch = chars.next();
}
if !decimal_size.is_empty() {
byte_size = match decimal_size.parse() {
Err(_) => {
return Err(format!(
"invalid number '{}' in format specification '{}'",
decimal_size, params
))
}
Ok(n) => n,
}
byte_size = decimal_size.parse().map_err(|_| {
format!(
"invalid number '{}' in format specification '{}'",
decimal_size, params
)
})?;
}
}
if is_format_dump_char(ch, &mut show_ascii_dump) {
ch = chars.next();
}
match od_format_type(type_char, byte_size) {
Some(ft) => formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump)),
None => {
return Err(format!(
"invalid size '{}' in format specification '{}'",
byte_size, params
))
}
}
let ft = od_format_type(type_char, byte_size).ok_or_else(|| {
format!(
"invalid size '{}' in format specification '{}'",
byte_size, params
)
})?;
formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump));
}
Ok(formats)
@ -335,16 +320,13 @@ pub fn parse_format_flags_str(
args_str: &Vec<&'static str>,
) -> Result<Vec<FormatterItemInfo>, String> {
let args: Vec<String> = args_str.iter().map(|s| s.to_string()).collect();
match parse_format_flags(&args) {
Err(e) => Err(e),
Ok(v) => {
// tests using this function assume add_ascii_dump is not set
Ok(v.into_iter()
.inspect(|f| assert!(!f.add_ascii_dump))
.map(|f| f.formatter_item_info)
.collect())
}
}
parse_format_flags(&args).map(|v| {
// tests using this function assume add_ascii_dump is not set
v.into_iter()
.inspect(|f| assert!(!f.add_ascii_dump))
.map(|f| f.formatter_item_info)
.collect()
})
}
#[test]

View file

@ -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(),

View file

@ -1,14 +1,17 @@
pub fn parse_number_of_bytes(s: &str) -> Result<usize, &'static str> {
use uucore::parse_size::{parse_size, ParseSizeError};
pub fn parse_number_of_bytes(s: &str) -> Result<usize, ParseSizeError> {
let mut start = 0;
let mut len = s.len();
let mut radix = 10;
let mut radix = 16;
let mut multiply = 1;
if s.starts_with("0x") || s.starts_with("0X") {
start = 2;
radix = 16;
} else if s.starts_with('0') {
radix = 8;
} else {
return parse_size(&s[start..]);
}
let mut ends_with = s.chars().rev();
@ -56,78 +59,33 @@ pub fn parse_number_of_bytes(s: &str) -> Result<usize, &'static str> {
Some('P') => 1000 * 1000 * 1000 * 1000 * 1000,
#[cfg(target_pointer_width = "64")]
Some('E') => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
_ => return Err("parse failed"),
_ => return Err(ParseSizeError::ParseFailure(s.to_string())),
}
}
_ => {}
}
match usize::from_str_radix(&s[start..len], radix) {
Ok(i) => Ok(i * multiply),
Err(_) => Err("parse failed"),
}
}
#[allow(dead_code)]
fn parse_number_of_bytes_str(s: &str) -> Result<usize, &'static str> {
parse_number_of_bytes(&String::from(s))
let factor = match usize::from_str_radix(&s[start..len], radix) {
Ok(f) => f,
Err(e) => return Err(ParseSizeError::ParseFailure(e.to_string())),
};
factor
.checked_mul(multiply)
.ok_or_else(|| ParseSizeError::SizeTooBig(s.to_string()))
}
#[test]
fn test_parse_number_of_bytes() {
// normal decimal numbers
assert_eq!(0, parse_number_of_bytes_str("0").unwrap());
assert_eq!(5, parse_number_of_bytes_str("5").unwrap());
assert_eq!(999, parse_number_of_bytes_str("999").unwrap());
assert_eq!(2 * 512, parse_number_of_bytes_str("2b").unwrap());
assert_eq!(2 * 1024, parse_number_of_bytes_str("2k").unwrap());
assert_eq!(4 * 1024, parse_number_of_bytes_str("4K").unwrap());
assert_eq!(2 * 1048576, parse_number_of_bytes_str("2m").unwrap());
assert_eq!(4 * 1048576, parse_number_of_bytes_str("4M").unwrap());
assert_eq!(1073741824, parse_number_of_bytes_str("1G").unwrap());
assert_eq!(2000, parse_number_of_bytes_str("2kB").unwrap());
assert_eq!(4000, parse_number_of_bytes_str("4KB").unwrap());
assert_eq!(2000000, parse_number_of_bytes_str("2mB").unwrap());
assert_eq!(4000000, parse_number_of_bytes_str("4MB").unwrap());
assert_eq!(2000000000, parse_number_of_bytes_str("2GB").unwrap());
// octal input
assert_eq!(8, parse_number_of_bytes_str("010").unwrap());
assert_eq!(8 * 512, parse_number_of_bytes_str("010b").unwrap());
assert_eq!(8 * 1024, parse_number_of_bytes_str("010k").unwrap());
assert_eq!(8 * 1048576, parse_number_of_bytes_str("010m").unwrap());
assert_eq!(8, parse_number_of_bytes("010").unwrap());
assert_eq!(8 * 512, parse_number_of_bytes("010b").unwrap());
assert_eq!(8 * 1024, parse_number_of_bytes("010k").unwrap());
assert_eq!(8 * 1_048_576, parse_number_of_bytes("010m").unwrap());
// hex input
assert_eq!(15, parse_number_of_bytes_str("0xf").unwrap());
assert_eq!(15, parse_number_of_bytes_str("0XF").unwrap());
assert_eq!(27, parse_number_of_bytes_str("0x1b").unwrap());
assert_eq!(16 * 1024, parse_number_of_bytes_str("0x10k").unwrap());
assert_eq!(16 * 1048576, parse_number_of_bytes_str("0x10m").unwrap());
// invalid input
parse_number_of_bytes_str("").unwrap_err();
parse_number_of_bytes_str("-1").unwrap_err();
parse_number_of_bytes_str("1e2").unwrap_err();
parse_number_of_bytes_str("xyz").unwrap_err();
parse_number_of_bytes_str("b").unwrap_err();
parse_number_of_bytes_str("1Y").unwrap_err();
parse_number_of_bytes_str("").unwrap_err();
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_parse_number_of_bytes_64bits() {
assert_eq!(1099511627776, parse_number_of_bytes_str("1T").unwrap());
assert_eq!(1125899906842624, parse_number_of_bytes_str("1P").unwrap());
assert_eq!(
1152921504606846976,
parse_number_of_bytes_str("1E").unwrap()
);
assert_eq!(2000000000000, parse_number_of_bytes_str("2TB").unwrap());
assert_eq!(2000000000000000, parse_number_of_bytes_str("2PB").unwrap());
assert_eq!(
2000000000000000000,
parse_number_of_bytes_str("2EB").unwrap()
);
assert_eq!(15, parse_number_of_bytes("0xf").unwrap());
assert_eq!(15, parse_number_of_bytes("0XF").unwrap());
assert_eq!(27, parse_number_of_bytes("0x1b").unwrap());
assert_eq!(16 * 1024, parse_number_of_bytes("0x10k").unwrap());
assert_eq!(16 * 1_048_576, parse_number_of_bytes("0x10m").unwrap());
}

View file

@ -36,16 +36,15 @@ impl<R: Read> Read for PartialReader<R> {
while self.skip > 0 {
let skip_count = cmp::min(self.skip, MAX_SKIP_BUFFER);
match self.inner.read(&mut bytes[..skip_count]) {
Ok(0) => {
match self.inner.read(&mut bytes[..skip_count])? {
0 => {
// this is an error as we still have more to skip
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"tried to skip past end of input",
));
}
Ok(n) => self.skip -= n,
Err(e) => return Err(e),
n => self.skip -= n,
}
}
}

View file

@ -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 '{}'",
@ -241,13 +241,14 @@ fn no_leading_hyphen(path_segment: &str) -> bool {
// check whether a path segment contains only valid (read: portable) characters
fn check_portable_chars(path_segment: &str) -> bool {
let valid_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-".to_string();
for ch in path_segment.chars() {
if !valid_str.contains(ch) {
const VALID_CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-";
for (i, ch) in path_segment.as_bytes().iter().enumerate() {
if !VALID_CHARS.contains(ch) {
let invalid = path_segment[i..].chars().next().unwrap();
writeln!(
&mut std::io::stderr(),
"nonportable character '{}' in file name component '{}'",
ch,
invalid,
path_segment
);
return false;

View file

@ -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() {

View file

@ -401,18 +401,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for file_group in file_groups {
let result_options = build_options(&matches, &file_group, args.join(" "));
let options = match result_options {
Ok(options) => options,
Err(err) => {
print_error(&matches, err);
return 1;
}
};
if result_options.is_err() {
print_error(&matches, result_options.err().unwrap());
return 1;
}
let options = &result_options.unwrap();
let cmd_result = if file_group.len() == 1 {
pr(&file_group.get(0).unwrap(), options)
let cmd_result = if let Ok(group) = file_group.iter().exactly_one() {
pr(group, &options)
} else {
mpr(&file_group, options)
mpr(&file_group, &options)
};
let status = match cmd_result {
@ -442,11 +442,12 @@ fn recreate_arguments(args: &[String]) -> Vec<String> {
let mut arguments = args.to_owned();
let num_option = args.iter().find_position(|x| n_regex.is_match(x.trim()));
if let Some((pos, _value)) = num_option {
let num_val_opt = args.get(pos + 1);
if num_val_opt.is_some() && !num_regex.is_match(num_val_opt.unwrap()) {
let could_be_file = arguments.remove(pos + 1);
arguments.insert(pos + 1, format!("{}", NumberingMode::default().width));
arguments.insert(pos + 2, could_be_file);
if let Some(num_val_opt) = args.get(pos + 1) {
if !num_regex.is_match(num_val_opt) {
let could_be_file = arguments.remove(pos + 1);
arguments.insert(pos + 1, format!("{}", NumberingMode::default().width));
arguments.insert(pos + 2, could_be_file);
}
}
}
@ -666,12 +667,13 @@ fn build_options(
None => end_page_in_plus_option,
};
if end_page.is_some() && start_page > end_page.unwrap() {
return Err(PrError::EncounteredErrors(format!(
"invalid --pages argument '{}:{}'",
start_page,
end_page.unwrap()
)));
if let Some(end_page) = end_page {
if start_page > end_page {
return Err(PrError::EncounteredErrors(format!(
"invalid --pages argument '{}:{}'",
start_page, end_page
)));
}
}
let default_lines_per_page = if form_feed_used {
@ -947,7 +949,7 @@ fn read_stream_and_create_pages(
let current_page = x + 1;
current_page >= start_page
&& (last_page.is_none() || current_page <= last_page.unwrap())
&& last_page.map_or(true, |last_page| current_page <= last_page)
}),
)
}
@ -996,8 +998,8 @@ fn mpr(paths: &[String], options: &OutputOptions) -> Result<i32, PrError> {
for (_key, file_line_group) in file_line_groups.into_iter() {
for file_line in file_line_group {
if file_line.line_content.is_err() {
return Err(file_line.line_content.unwrap_err().into());
if let Err(e) = file_line.line_content {
return Err(e.into());
}
let new_page_number = file_line.page_number;
if page_counter != new_page_number {
@ -1030,8 +1032,7 @@ fn print_page(lines: &[FileLine], options: &OutputOptions, page: usize) -> Resul
let lines_written = write_columns(lines, options, out)?;
for index in 0..trailer_content.len() {
let x = trailer_content.get(index).unwrap();
for (index, x) in trailer_content.iter().enumerate() {
out.write_all(x.as_bytes())?;
if index + 1 != trailer_content.len() {
out.write_all(line_separator)?;
@ -1074,8 +1075,7 @@ fn write_columns(
let mut offset = 0;
for col in 0..columns {
let mut inserted = 0;
for i in offset..lines.len() {
let line = lines.get(i).unwrap();
for line in &lines[offset..] {
if line.file_id != col {
break;
}
@ -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!(
"{}{}",

View file

@ -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,

View file

@ -55,18 +55,9 @@ impl Formatter for Decf {
);
// strip trailing zeroes
if let Some(ref post_dec) = f_sci.post_decimal {
let mut i = post_dec.len();
{
let mut it = post_dec.chars();
while let Some(c) = it.next_back() {
if c != '0' {
break;
}
i -= 1;
}
}
if i != post_dec.len() {
f_sci.post_decimal = Some(String::from(&post_dec[0..i]));
let trimmed = post_dec.trim_end_matches('0');
if trimmed.len() != post_dec.len() {
f_sci.post_decimal = Some(trimmed.to_owned());
}
}
let f_fl = get_primitive_dec(

View file

@ -247,8 +247,12 @@ pub fn get_primitive_dec(
first_segment.len() as isize - 1,
)
} else {
match first_segment.chars().next() {
Some('0') => {
match first_segment
.chars()
.next()
.expect("float_common: no chars in first segment.")
{
'0' => {
let it = second_segment.chars().enumerate();
let mut m: isize = 0;
let mut pre = String::from("0");
@ -266,10 +270,7 @@ pub fn get_primitive_dec(
}
(pre, post, m)
}
Some(_) => (first_segment, second_segment, 0),
None => {
panic!("float_common: no chars in first segment.");
}
_ => (first_segment, second_segment, 0),
}
}
} else {
@ -298,11 +299,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!(

View file

@ -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),

View file

@ -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!(

View file

@ -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)

View file

@ -275,7 +275,7 @@ impl SubParser {
}
None => {
text_so_far.push('%');
err_conv(&text_so_far);
err_conv(text_so_far);
false
}
}

View file

@ -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
}

View file

@ -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"

View file

@ -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)

View file

@ -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() {
@ -109,17 +109,14 @@ fn remove(dirs: Vec<String>, ignore: bool, parents: bool, verbose: bool) -> Resu
}
fn remove_dir(path: &Path, ignore: bool, verbose: bool) -> Result<(), i32> {
let mut read_dir = match fs::read_dir(path) {
Ok(m) => m,
Err(e) if e.raw_os_error() == Some(ENOTDIR) => {
let mut read_dir = fs::read_dir(path).map_err(|e| {
if e.raw_os_error() == Some(ENOTDIR) {
show_error!("failed to remove '{}': Not a directory", path.display());
return Err(1);
}
Err(e) => {
} else {
show_error!("reading directory '{}': {}", path.display(), e);
return Err(1);
}
};
1
})?;
let mut r = Ok(());

View file

@ -24,7 +24,7 @@ extern crate uucore;
static NAME: &str = "shred";
const BLOCK_SIZE: usize = 512;
const NAME_CHARSET: &str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
const NAME_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
// Patterns as shown in the GNU coreutils shred implementation
const PATTERNS: [&[u8]; 22] = [
@ -89,7 +89,7 @@ impl Iterator for FilenameGenerator {
// Make the return value, then increment
let mut ret = String::new();
for i in name_charset_indices.iter() {
let c: char = NAME_CHARSET.chars().nth(*i).unwrap();
let c = char::from(NAME_CHARSET[*i]);
ret.push(c);
}
@ -163,16 +163,14 @@ impl<'a> BytesGenerator<'a> {
return None;
}
let this_block_size = {
if !self.exact {
let this_block_size = if !self.exact {
self.block_size
} else {
let bytes_left = self.total_bytes - self.bytes_generated.get();
if bytes_left >= self.block_size as u64 {
self.block_size
} else {
let bytes_left = self.total_bytes - self.bytes_generated.get();
if bytes_left >= self.block_size as u64 {
self.block_size
} else {
(bytes_left % self.block_size as u64) as usize
}
(bytes_left % self.block_size as u64) as usize
}
};
@ -184,12 +182,10 @@ impl<'a> BytesGenerator<'a> {
rng.fill(bytes);
}
PassType::Pattern(pattern) => {
let skip = {
if self.bytes_generated.get() == 0 {
0
} else {
(pattern.len() as u64 % self.bytes_generated.get()) as usize
}
let skip = if self.bytes_generated.get() == 0 {
0
} else {
(pattern.len() as u64 % self.bytes_generated.get()) as usize
};
// Copy the pattern in chunks rather than simply one byte at a time
@ -381,7 +377,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 +655,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)?;
}

View file

@ -285,14 +285,12 @@ fn parse_range(input_range: &str) -> Result<(usize, usize), String> {
if split.len() != 2 {
Err(format!("invalid input range: '{}'", input_range))
} else {
let begin = match split[0].parse::<usize>() {
Ok(m) => m,
Err(_) => return Err(format!("invalid input range: '{}'", split[0])),
};
let end = match split[1].parse::<usize>() {
Ok(m) => m,
Err(_) => return Err(format!("invalid input range: '{}'", split[1])),
};
let begin = split[0]
.parse::<usize>()
.map_err(|_| format!("invalid input range: '{}'", split[0]))?;
let end = split[1]
.parse::<usize>()
.map_err(|_| format!("invalid input range: '{}'", split[1]))?;
Ok((begin, end + 1))
}
}

View file

@ -16,7 +16,7 @@ path = "src/sleep.rs"
[dependencies]
clap = "2.33"
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["parse_time"] }
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
[[bin]]

View file

@ -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);
}

View file

@ -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,
@ -110,7 +110,7 @@ pub fn read(
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);
parse_lines(read, &mut lines, separator, settings);
lines
});
sender.send(payload).unwrap();
@ -194,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.

View file

@ -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()

View file

@ -43,6 +43,7 @@ use std::ops::Range;
use std::path::Path;
use std::path::PathBuf;
use unicode_width::UnicodeWidthStr;
use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::InvalidEncodingHandling;
static NAME: &str = "sort";
@ -162,32 +163,29 @@ pub struct GlobalSettings {
}
impl GlobalSettings {
/// Interpret this `&str` as a number with an optional trailing si unit.
///
/// If there is no trailing si unit, the implicit unit is K.
/// The suffix B causes the number to be interpreted as a byte count.
fn parse_byte_count(input: &str) -> usize {
const SI_UNITS: &[char] = &['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
/// Parse a SIZE string into a number of bytes.
/// A size string comprises an integer and an optional unit.
/// The unit may be k, K, m, M, g, G, t, T, P, E, Z, Y (powers of 1024), or b which is 1.
/// Default is K.
fn parse_byte_count(input: &str) -> Result<usize, ParseSizeError> {
// GNU sort (8.32) valid: 1b, k, K, m, M, g, G, t, T, P, E, Z, Y
// GNU sort (8.32) invalid: b, B, 1B, p, e, z, y
const ALLOW_LIST: &[char] = &[
'b', 'k', 'K', 'm', 'M', 'g', 'G', 't', 'T', 'P', 'E', 'Z', 'Y',
];
let mut size_string = input.trim().to_string();
let input = input.trim();
let (num_str, si_unit) =
if input.ends_with(|c: char| SI_UNITS.contains(&c.to_ascii_uppercase())) {
let mut chars = input.chars();
let si_suffix = chars.next_back().unwrap().to_ascii_uppercase();
let si_unit = SI_UNITS.iter().position(|&c| c == si_suffix).unwrap();
let num_str = chars.as_str();
(num_str, si_unit)
} else {
(input, 1)
};
let num_usize: usize = num_str
.trim()
.parse()
.unwrap_or_else(|e| crash!(1, "failed to parse buffer size `{}`: {}", num_str, e));
num_usize.saturating_mul(1000usize.saturating_pow(si_unit as u32))
if size_string.ends_with(|c: char| ALLOW_LIST.contains(&c) || c.is_digit(10)) {
// b 1, K 1024 (default)
if size_string.ends_with(|c: char| c.is_digit(10)) {
size_string.push('K');
} else if size_string.ends_with('b') {
size_string.pop();
}
parse_size(&size_string)
} else {
Err(ParseSizeError::ParseFailure("invalid suffix".to_string()))
}
}
fn out_writer(&self) -> BufWriter<Box<dyn Write>> {
@ -400,9 +398,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
@ -756,7 +754,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
{
@ -846,7 +844,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)) => {
@ -1176,8 +1174,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
settings.buffer_size = matches
.value_of(OPT_BUF_SIZE)
.map(GlobalSettings::parse_byte_count)
.unwrap_or(DEFAULT_BUF_SIZE);
.map_or(DEFAULT_BUF_SIZE, |s| {
GlobalSettings::parse_byte_count(s)
.unwrap_or_else(|e| crash!(2, "{}", format_error_message(e, s, OPT_BUF_SIZE)))
});
settings.tmp_dir = matches
.value_of(OPT_TMP_DIR)
@ -1257,11 +1257,11 @@ 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);
}
}
@ -1277,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))
}
}
@ -1587,6 +1587,16 @@ fn open(path: impl AsRef<OsStr>) -> Box<dyn Read + Send> {
}
}
fn format_error_message(error: ParseSizeError, s: &str, option: &str) -> String {
// NOTE:
// GNU's sort echos affected flag, -S or --buffer-size, depending user's selection
// GNU's sort does distinguish between "invalid (suffix in) argument"
match error {
ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s),
ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s),
}
}
#[cfg(test)]
mod tests {
@ -1676,4 +1686,48 @@ mod tests {
// How big is a selection? Constant cost all lines pay when we need selections.
assert_eq!(std::mem::size_of::<Selection>(), 24);
}
#[test]
fn test_parse_byte_count() {
let valid_input = [
("0", 0),
("50K", 50 * 1024),
("50k", 50 * 1024),
("1M", 1024 * 1024),
("100M", 100 * 1024 * 1024),
#[cfg(not(target_pointer_width = "32"))]
("1000G", 1000 * 1024 * 1024 * 1024),
#[cfg(not(target_pointer_width = "32"))]
("10T", 10 * 1024 * 1024 * 1024 * 1024),
("1b", 1),
("1024b", 1024),
("1024Mb", 1024 * 1024 * 1024), // NOTE: This might not be how GNU `sort` behaves for 'Mb'
("1", 1024), // K is default
("50", 50 * 1024),
("K", 1024),
("k", 1024),
("m", 1024 * 1024),
#[cfg(not(target_pointer_width = "32"))]
("E", 1024 * 1024 * 1024 * 1024 * 1024 * 1024),
];
for (input, expected_output) in &valid_input {
assert_eq!(
GlobalSettings::parse_byte_count(input),
Ok(*expected_output)
);
}
// SizeTooBig
let invalid_input = ["500E", "1Y"];
for input in &invalid_input {
#[cfg(not(target_pointer_width = "128"))]
assert!(GlobalSettings::parse_byte_count(input).is_err());
}
// ParseFailure
let invalid_input = ["nonsense", "1B", "B", "b", "p", "e", "z", "y"];
for input in &invalid_input {
assert!(GlobalSettings::parse_byte_count(input).is_err());
}
}
}

View file

@ -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>),
}
}

View file

@ -13,11 +13,13 @@ extern crate uucore;
mod platform;
use clap::{crate_version, App, Arg};
use std::convert::TryFrom;
use std::env;
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;
use std::{char, fs::remove_file};
use uucore::parse_size::parse_size;
static NAME: &str = "split";
@ -231,10 +233,9 @@ struct LineSplitter {
impl LineSplitter {
fn new(settings: &Settings) -> LineSplitter {
LineSplitter {
lines_per_split: settings
.strategy_param
.parse()
.unwrap_or_else(|e| crash!(1, "invalid number of lines: {}", e)),
lines_per_split: settings.strategy_param.parse().unwrap_or_else(|_| {
crash!(1, "invalid number of lines: {}", settings.strategy_param)
}),
}
}
}
@ -276,40 +277,14 @@ struct ByteSplitter {
impl ByteSplitter {
fn new(settings: &Settings) -> ByteSplitter {
// These multipliers are the same as supported by GNU coreutils.
let modifiers: Vec<(&str, u128)> = vec![
("K", 1024u128),
("M", 1024 * 1024),
("G", 1024 * 1024 * 1024),
("T", 1024 * 1024 * 1024 * 1024),
("P", 1024 * 1024 * 1024 * 1024 * 1024),
("E", 1024 * 1024 * 1024 * 1024 * 1024 * 1024),
("Z", 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024),
("Y", 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024),
("KB", 1000),
("MB", 1000 * 1000),
("GB", 1000 * 1000 * 1000),
("TB", 1000 * 1000 * 1000 * 1000),
("PB", 1000 * 1000 * 1000 * 1000 * 1000),
("EB", 1000 * 1000 * 1000 * 1000 * 1000 * 1000),
("ZB", 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000),
("YB", 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000),
];
// This sequential find is acceptable since none of the modifiers are
// suffixes of any other modifiers, a la Huffman codes.
let (suffix, multiplier) = modifiers
.iter()
.find(|(suffix, _)| settings.strategy_param.ends_with(suffix))
.unwrap_or(&("", 1));
// Try to parse the actual numeral.
let n = &settings.strategy_param[0..(settings.strategy_param.len() - suffix.len())]
.parse::<u128>()
.unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e));
let size_string = &settings.strategy_param;
let size_num = match parse_size(size_string) {
Ok(n) => n,
Err(e) => crash!(1, "invalid number of bytes: {}", e.to_string()),
};
ByteSplitter {
bytes_per_split: n * multiplier,
bytes_per_split: u128::try_from(size_num).unwrap(),
}
}
}

View file

@ -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)

View file

@ -19,6 +19,7 @@ use std::path::PathBuf;
use std::process::Command;
use tempfile::tempdir;
use tempfile::TempDir;
use uucore::parse_size::parse_size;
use uucore::InvalidEncodingHandling;
static ABOUT: &str =
@ -55,7 +56,7 @@ const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf
enum BufferType {
Default,
Line,
Size(u64),
Size(usize),
}
struct ProgramOptions {
@ -69,9 +70,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)?,
})
}
}
@ -104,41 +105,6 @@ fn preload_strings() -> (&'static str, &'static str) {
crash!(1, "Command not supported for this operating system!")
}
fn parse_size(size: &str) -> Option<u64> {
let ext = size.trim_start_matches(|c: char| c.is_digit(10));
let num = size.trim_end_matches(char::is_alphabetic);
let mut recovered = num.to_owned();
recovered.push_str(ext);
if recovered != size {
return None;
}
let buf_size: u64 = match num.parse().ok() {
Some(m) => m,
None => return None,
};
let (power, base): (u32, u64) = match ext {
"" => (0, 0),
"KB" => (1, 1024),
"K" => (1, 1000),
"MB" => (2, 1024),
"M" => (2, 1000),
"GB" => (3, 1024),
"G" => (3, 1000),
"TB" => (4, 1024),
"T" => (4, 1000),
"PB" => (5, 1024),
"P" => (5, 1000),
"EB" => (6, 1024),
"E" => (6, 1000),
"ZB" => (7, 1024),
"Z" => (7, 1000),
"YB" => (8, 1024),
"Y" => (8, 1000),
_ => return None,
};
Some(buf_size * base.pow(power))
}
fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
match matches.value_of(name) {
Some(value) => match value {
@ -151,13 +117,10 @@ fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramO
Ok(BufferType::Line)
}
}
x => {
let size = match parse_size(x) {
Some(m) => m,
None => return Err(ProgramOptionsError(format!("invalid mode {}", x))),
};
Ok(BufferType::Size(size))
}
x => parse_size(x).map_or_else(
|e| crash!(125, "invalid mode {}", e),
|m| Ok(BufferType::Size(m)),
),
},
None => Ok(BufferType::Default),
}

View file

@ -5,7 +5,6 @@
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// *
// spell-checker:ignore (ToDO) seekable seek'd tail'ing ringbuffer ringbuf
@ -21,19 +20,18 @@ use chunks::ReverseChunks;
use clap::{App, Arg};
use std::collections::VecDeque;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;
use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::ringbuffer::RingBuffer;
pub mod options {
pub mod verbosity {
pub static QUIET: &str = "quiet";
pub static SILENT: &str = "silent";
pub static VERBOSE: &str = "verbose";
}
pub static BYTES: &str = "bytes";
@ -42,13 +40,12 @@ pub mod options {
pub static PID: &str = "pid";
pub static SLEEP_INT: &str = "sleep-interval";
pub static ZERO_TERM: &str = "zero-terminated";
pub static ARG_FILES: &str = "files";
}
static ARG_FILES: &str = "files";
enum FilterMode {
Bytes(u64),
Lines(u64, u8), // (number of lines, delimiter)
Bytes(usize),
Lines(usize, u8), // (number of lines, delimiter)
}
struct Settings {
@ -78,12 +75,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let app = App::new(executable!())
.version(crate_version!())
.about("output the last part of files")
// TODO: add usage
.arg(
Arg::with_name(options::BYTES)
.short("c")
.long(options::BYTES)
.takes_value(true)
.allow_hyphen_values(true)
.overrides_with_all(&[options::BYTES, options::LINES])
.help("Number of bytes to print"),
)
.arg(
@ -98,6 +97,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.long(options::LINES)
.takes_value(true)
.allow_hyphen_values(true)
.overrides_with_all(&[options::BYTES, options::LINES])
.help("Number of lines to print"),
)
.arg(
@ -110,13 +110,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::verbosity::QUIET)
.short("q")
.long(options::verbosity::QUIET)
.visible_alias("silent")
.overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE])
.help("never output headers giving file names"),
)
.arg(
Arg::with_name(options::verbosity::SILENT)
.long(options::verbosity::SILENT)
.help("synonym of --quiet"),
)
.arg(
Arg::with_name(options::SLEEP_INT)
.short("s")
@ -128,6 +125,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::verbosity::VERBOSE)
.short("v")
.long(options::verbosity::VERBOSE)
.overrides_with_all(&[options::verbosity::QUIET, options::verbosity::VERBOSE])
.help("always output headers giving file names"),
)
.arg(
@ -137,7 +135,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.help("Line delimiter is NUL, not newline"),
)
.arg(
Arg::with_name(ARG_FILES)
Arg::with_name(options::ARG_FILES)
.multiple(true)
.takes_value(true)
.min_values(1),
@ -171,38 +169,21 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
}
match matches.value_of(options::LINES) {
Some(n) => {
let mut slice: &str = n;
if slice.chars().next().unwrap_or('_') == '+' {
settings.beginning = true;
slice = &slice[1..];
}
match parse_size(slice) {
Ok(m) => settings.mode = FilterMode::Lines(m, b'\n'),
Err(e) => {
show_error!("{}", e.to_string());
return 1;
}
}
let mode_and_beginning = if let Some(arg) = matches.value_of(options::BYTES) {
match parse_num(arg) {
Ok((n, beginning)) => (FilterMode::Bytes(n), beginning),
Err(e) => crash!(1, "invalid number of bytes: {}", e.to_string()),
}
None => {
if let Some(n) = matches.value_of(options::BYTES) {
let mut slice: &str = n;
if slice.chars().next().unwrap_or('_') == '+' {
settings.beginning = true;
slice = &slice[1..];
}
match parse_size(slice) {
Ok(m) => settings.mode = FilterMode::Bytes(m),
Err(e) => {
show_error!("{}", e.to_string());
return 1;
}
}
}
} else if let Some(arg) = matches.value_of(options::LINES) {
match parse_num(arg) {
Ok((n, beginning)) => (FilterMode::Lines(n, b'\n'), beginning),
Err(e) => crash!(1, "invalid number of lines: {}", e.to_string()),
}
} else {
(FilterMode::Lines(10, b'\n'), false)
};
settings.mode = mode_and_beginning.0;
settings.beginning = mode_and_beginning.1;
if matches.is_present(options::ZERO_TERM) {
if let FilterMode::Lines(count, _) = settings.mode {
@ -211,11 +192,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
let verbose = matches.is_present(options::verbosity::VERBOSE);
let quiet = matches.is_present(options::verbosity::QUIET)
|| matches.is_present(options::verbosity::SILENT);
let quiet = matches.is_present(options::verbosity::QUIET);
let files: Vec<String> = matches
.values_of(ARG_FILES)
.values_of(options::ARG_FILES)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
@ -264,98 +244,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
0
}
#[derive(Debug, PartialEq, Eq)]
pub enum ParseSizeErr {
ParseFailure(String),
SizeTooBig(String),
}
impl Error for ParseSizeErr {
fn description(&self) -> &str {
match *self {
ParseSizeErr::ParseFailure(ref s) => &*s,
ParseSizeErr::SizeTooBig(ref s) => &*s,
}
}
}
impl fmt::Display for ParseSizeErr {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let s = match self {
ParseSizeErr::ParseFailure(s) => s,
ParseSizeErr::SizeTooBig(s) => s,
};
write!(f, "{}", s)
}
}
impl ParseSizeErr {
fn parse_failure(s: &str) -> ParseSizeErr {
ParseSizeErr::ParseFailure(format!("invalid size: '{}'", s))
}
fn size_too_big(s: &str) -> ParseSizeErr {
ParseSizeErr::SizeTooBig(format!(
"invalid size: '{}': Value too large to be stored in data type",
s
))
}
}
pub type ParseSizeResult = Result<u64, ParseSizeErr>;
pub fn parse_size(mut size_slice: &str) -> Result<u64, ParseSizeErr> {
let mut base = if size_slice.chars().last().unwrap_or('_') == 'B' {
size_slice = &size_slice[..size_slice.len() - 1];
1000u64
} else {
1024u64
};
let exponent = if !size_slice.is_empty() {
let mut has_suffix = true;
let exp = match size_slice.chars().last().unwrap_or('_') {
'K' | 'k' => 1u64,
'M' => 2u64,
'G' => 3u64,
'T' => 4u64,
'P' => 5u64,
'E' => 6u64,
'Z' | 'Y' => {
return Err(ParseSizeErr::size_too_big(size_slice));
}
'b' => {
base = 512u64;
1u64
}
_ => {
has_suffix = false;
0u64
}
};
if has_suffix {
size_slice = &size_slice[..size_slice.len() - 1];
}
exp
} else {
0u64
};
let mut multiplier = 1u64;
for _ in 0u64..exponent {
multiplier *= base;
}
if base == 1000u64 && exponent == 0u64 {
// sole B is not a valid suffix
Err(ParseSizeErr::parse_failure(size_slice))
} else {
let value: Option<i64> = size_slice.parse().ok();
value
.map(|v| Ok((multiplier as i64 * v.abs()) as u64))
.unwrap_or_else(|| Err(ParseSizeErr::parse_failure(size_slice)))
}
}
fn follow<T: Read>(readers: &mut [BufReader<T>], filenames: &[String], settings: &Settings) {
assert!(settings.follow);
let mut last = readers.len() - 1;
@ -469,7 +357,7 @@ fn bounded_tail(file: &mut File, settings: &Settings) {
/// If any element of `iter` is an [`Err`], then this function panics.
fn unbounded_tail_collect<T, E>(
iter: impl Iterator<Item = Result<T, E>>,
count: u64,
count: usize,
beginning: bool,
) -> VecDeque<T>
where
@ -514,3 +402,22 @@ fn print_byte<T: Write>(stdout: &mut T, ch: u8) {
crash!(1, "{}", err);
}
}
fn parse_num(src: &str) -> Result<(usize, bool), ParseSizeError> {
let mut size_string = src.trim();
let mut starting_with = false;
if let Some(c) = size_string.chars().next() {
if c == '+' || c == '-' {
// tail: '-' is not documented (8.32 man pages)
size_string = &size_string[1..];
if c == '+' {
starting_with = true;
}
}
} else {
return Err(ParseSizeError::ParseFailure(src.to_string()));
}
parse_size(size_string).map(|n| (n, starting_with))
}

View file

@ -16,9 +16,8 @@ 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 = { version=">=0.0.8", package="uucore", path="../../uucore", features=["process", "signals"] }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }

View file

@ -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),
};

View file

@ -22,14 +22,15 @@ use std::ops::RangeInclusive;
/// character; octal escape sequences consume 1 to 3 octal digits.
#[inline]
fn parse_sequence(s: &str) -> (char, usize) {
let c = s.chars().next().expect("invalid escape: empty string");
let mut s = s.chars();
let c = s.next().expect("invalid escape: empty string");
if ('0'..='7').contains(&c) {
let mut v = c.to_digit(8).unwrap();
let mut consumed = 1;
let bits_per_digit = 3;
for c in s.chars().skip(1).take(2) {
for c in s.take(2) {
match c.to_digit(8) {
Some(c) => {
v = (v << bits_per_digit) | c;

View file

@ -11,19 +11,21 @@
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::convert::TryFrom;
use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;
use uucore::parse_size::{parse_size, ParseSizeError};
#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
Absolute(u64),
Extend(u64),
Reduce(u64),
AtMost(u64),
AtLeast(u64),
RoundDown(u64),
RoundUp(u64),
Absolute(usize),
Extend(usize),
Reduce(usize),
AtMost(usize),
AtLeast(usize),
RoundDown(usize),
RoundUp(usize),
}
impl TruncateMode {
@ -38,7 +40,7 @@ impl TruncateMode {
/// let fsize = 10;
/// assert_eq!(mode.to_size(fsize), 15);
/// ```
fn to_size(&self, fsize: u64) -> u64 {
fn to_size(&self, fsize: usize) -> usize {
match self {
TruncateMode::Absolute(size) => *size,
TruncateMode::Extend(size) => fsize + size,
@ -58,10 +60,9 @@ pub mod options {
pub static NO_CREATE: &str = "no-create";
pub static REFERENCE: &str = "reference";
pub static SIZE: &str = "size";
pub static ARG_FILES: &str = "files";
}
static ARG_FILES: &str = "files";
fn get_usage() -> String {
format!("{0} [OPTION]... [FILE]...", executable!())
}
@ -113,21 +114,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::REFERENCE)
.short("r")
.long(options::REFERENCE)
.required_unless(options::SIZE)
.help("base the size of each file on the size of RFILE")
.value_name("RFILE")
)
.arg(
Arg::with_name(options::SIZE)
.short("s")
.long("size")
.long(options::SIZE)
.required_unless(options::REFERENCE)
.help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
.value_name("SIZE")
)
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1))
.arg(Arg::with_name(options::ARG_FILES)
.value_name("FILE")
.multiple(true)
.takes_value(true)
.required(true)
.min_values(1))
.get_matches_from(args);
let files: Vec<String> = matches
.values_of(ARG_FILES)
.values_of(options::ARG_FILES)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
@ -149,8 +157,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
crash!(
1,
"cannot stat '{}': No such file or directory",
reference.unwrap()
);
reference.unwrap_or_else(|| "".to_string())
); // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size
}
_ => crash!(1, "{}", e.to_string()),
}
@ -172,10 +180,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
///
/// If the file could not be opened, or there was a problem setting the
/// size of the file.
fn file_truncate(filename: &str, create: bool, size: u64) -> std::io::Result<()> {
fn file_truncate(filename: &str, create: bool, size: usize) -> std::io::Result<()> {
let path = Path::new(filename);
let f = OpenOptions::new().write(true).create(create).open(path)?;
f.set_len(size)
f.set_len(u64::try_from(size).unwrap())
}
/// Truncate files to a size relative to a given file.
@ -206,9 +214,9 @@ fn truncate_reference_and_size(
}
_ => m,
},
Err(_) => crash!(1, "Invalid number: {}", size_string),
Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
};
let fsize = metadata(rfilename)?.len();
let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
let tsize = mode.to_size(fsize);
for filename in &filenames {
file_truncate(filename, create, tsize)?;
@ -232,7 +240,7 @@ fn truncate_reference_file_only(
filenames: Vec<String>,
create: bool,
) -> std::io::Result<()> {
let tsize = metadata(rfilename)?.len();
let tsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
for filename in &filenames {
file_truncate(filename, create, tsize)?;
}
@ -261,10 +269,10 @@ fn truncate_size_only(
) -> std::io::Result<()> {
let mode = match parse_mode_and_size(size_string) {
Ok(m) => m,
Err(_) => crash!(1, "Invalid number: {}", size_string),
Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
};
for filename in &filenames {
let fsize = metadata(filename).map(|m| m.len()).unwrap_or(0);
let fsize = usize::try_from(metadata(filename)?.len()).unwrap();
let tsize = mode.to_size(fsize);
file_truncate(filename, create, tsize)?;
}
@ -290,7 +298,7 @@ fn truncate(
}
(Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create),
(None, Some(size_string)) => truncate_size_only(&size_string, filenames, create),
(None, None) => crash!(1, "you must specify either --reference or --size"),
(None, None) => crash!(1, "you must specify either --reference or --size"), // this case cannot happen anymore because it's handled by clap
}
}
@ -317,117 +325,35 @@ fn is_modifier(c: char) -> bool {
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ()> {
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ParseSizeError> {
// Trim any whitespace.
let size_string = size_string.trim();
let mut size_string = size_string.trim();
// Get the modifier character from the size string, if any. For
// example, if the argument is "+123", then the modifier is '+'.
let c = size_string.chars().next().unwrap();
let size_string = if is_modifier(c) {
&size_string[1..]
if let Some(c) = size_string.chars().next() {
if is_modifier(c) {
size_string = &size_string[1..];
}
parse_size(size_string).map(match c {
'+' => TruncateMode::Extend,
'-' => TruncateMode::Reduce,
'<' => TruncateMode::AtMost,
'>' => TruncateMode::AtLeast,
'/' => TruncateMode::RoundDown,
'%' => TruncateMode::RoundUp,
_ => TruncateMode::Absolute,
})
} else {
size_string
};
parse_size(size_string).map(match c {
'+' => TruncateMode::Extend,
'-' => TruncateMode::Reduce,
'<' => TruncateMode::AtMost,
'>' => TruncateMode::AtLeast,
'/' => TruncateMode::RoundDown,
'%' => TruncateMode::RoundUp,
_ => TruncateMode::Absolute,
})
}
/// Parse a size string into a number of bytes.
///
/// A size string comprises an integer and an optional unit. The unit
/// may be K, M, G, T, P, E, Z, or Y (powers of 1024) or KB, MB,
/// etc. (powers of 1000).
///
/// # Errors
///
/// This function returns an error if the string does not begin with a
/// numeral, or if the unit is not one of the supported units described
/// in the preceding section.
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_size("123").unwrap(), 123);
/// assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
/// assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
/// ```
fn parse_size(size: &str) -> Result<u64, ()> {
// Get the numeric part of the size argument. For example, if the
// argument is "123K", then the numeric part is "123".
let numeric_string: String = size.chars().take_while(|c| c.is_digit(10)).collect();
let number: u64 = match numeric_string.parse() {
Ok(n) => n,
Err(_) => return Err(()),
};
// Get the alphabetic units part of the size argument and compute
// the factor it represents. For example, if the argument is "123K",
// then the unit part is "K" and the factor is 1024. This may be the
// empty string, in which case, the factor is 1.
let n = numeric_string.len();
let (base, exponent): (u64, u32) = match &size[n..] {
"" => (1, 0),
"K" | "k" => (1024, 1),
"M" | "m" => (1024, 2),
"G" | "g" => (1024, 3),
"T" | "t" => (1024, 4),
"P" | "p" => (1024, 5),
"E" | "e" => (1024, 6),
"Z" | "z" => (1024, 7),
"Y" | "y" => (1024, 8),
"KB" | "kB" => (1000, 1),
"MB" | "mB" => (1000, 2),
"GB" | "gB" => (1000, 3),
"TB" | "tB" => (1000, 4),
"PB" | "pB" => (1000, 5),
"EB" | "eB" => (1000, 6),
"ZB" | "zB" => (1000, 7),
"YB" | "yB" => (1000, 8),
_ => return Err(()),
};
let factor = base.pow(exponent);
Ok(number * factor)
Err(ParseSizeError::ParseFailure(size_string.to_string()))
}
}
#[cfg(test)]
mod tests {
use crate::parse_mode_and_size;
use crate::parse_size;
use crate::TruncateMode;
#[test]
fn test_parse_size_zero() {
assert_eq!(parse_size("0").unwrap(), 0);
assert_eq!(parse_size("0K").unwrap(), 0);
assert_eq!(parse_size("0KB").unwrap(), 0);
}
#[test]
fn test_parse_size_without_factor() {
assert_eq!(parse_size("123").unwrap(), 123);
}
#[test]
fn test_parse_size_kilobytes() {
assert_eq!(parse_size("123K").unwrap(), 123 * 1024);
assert_eq!(parse_size("123KB").unwrap(), 123 * 1000);
}
#[test]
fn test_parse_size_megabytes() {
assert_eq!(parse_size("123").unwrap(), 123);
assert_eq!(parse_size("123M").unwrap(), 123 * 1024 * 1024);
assert_eq!(parse_size("123MB").unwrap(), 123 * 1000 * 1000);
}
#[test]
fn test_parse_mode_and_size() {
assert_eq!(parse_mode_and_size("10"), Ok(TruncateMode::Absolute(10)));

View file

@ -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" }

View file

@ -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

View file

@ -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()
});

View file

@ -179,124 +179,58 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
// Ignored for 'who am i'.
let short_list = matches.is_present(options::COUNT);
// If true, display only name, line, and time fields.
let mut short_output = false;
// If true, display the hours:minutes since each user has touched
// the keyboard, or "." if within the last minute, or "old" if
// not within the last day.
let mut include_idle = false;
let all = matches.is_present(options::ALL);
// If true, display a line at the top describing each field.
let include_heading = matches.is_present(options::HEADING);
// If true, display a '+' for each user if mesg y, a '-' if mesg n,
// or a '?' if their tty cannot be statted.
let include_mesg = matches.is_present(options::ALL)
|| matches.is_present(options::MESG)
|| matches.is_present("w");
// If true, display process termination & exit status.
let mut include_exit = false;
let include_mesg = all || matches.is_present(options::MESG) || matches.is_present("w");
// If true, display the last boot time.
let mut need_boottime = false;
let need_boottime = all || matches.is_present(options::BOOT);
// If true, display dead processes.
let mut need_deadprocs = false;
let need_deadprocs = all || matches.is_present(options::DEAD);
// If true, display processes waiting for user login.
let mut need_login = false;
let need_login = all || matches.is_present(options::LOGIN);
// If true, display processes started by init.
let mut need_initspawn = false;
let need_initspawn = all || matches.is_present(options::PROCESS);
// If true, display the last clock change.
let mut need_clockchange = false;
let need_clockchange = all || matches.is_present(options::TIME);
// If true, display the current runlevel.
let mut need_runlevel = false;
let need_runlevel = all || matches.is_present(options::RUNLEVEL);
let use_defaults = !(all
|| need_boottime
|| need_deadprocs
|| need_login
|| need_initspawn
|| need_runlevel
|| need_clockchange
|| matches.is_present(options::USERS));
// If true, display user processes.
let mut need_users = false;
let need_users = all || matches.is_present(options::USERS) || use_defaults;
// If true, display the hours:minutes since each user has touched
// the keyboard, or "." if within the last minute, or "old" if
// not within the last day.
let include_idle = need_deadprocs || need_login || need_runlevel || need_users;
// If true, display process termination & exit status.
let include_exit = need_deadprocs;
// If true, display only name, line, and time fields.
let short_output = !include_exit && use_defaults;
// If true, display info only for the controlling tty.
let mut my_line_only = false;
let mut assumptions = true;
#[allow(clippy::useless_let_if_seq)]
{
if matches.is_present(options::ALL) {
need_boottime = true;
need_deadprocs = true;
need_login = true;
need_initspawn = true;
need_runlevel = true;
need_clockchange = true;
need_users = true;
include_idle = true;
include_exit = true;
assumptions = false;
}
if matches.is_present(options::BOOT) {
need_boottime = true;
assumptions = false;
}
if matches.is_present(options::DEAD) {
need_deadprocs = true;
include_idle = true;
include_exit = true;
assumptions = false;
}
if matches.is_present(options::LOGIN) {
need_login = true;
include_idle = true;
assumptions = false;
}
if matches.is_present(options::ONLY_HOSTNAME_USER) || files.len() == 2 {
my_line_only = true;
}
if matches.is_present(options::PROCESS) {
need_initspawn = true;
assumptions = false;
}
if matches.is_present(options::RUNLEVEL) {
need_runlevel = true;
include_idle = true;
assumptions = false;
}
if matches.is_present(options::SHORT) {
short_output = true;
}
if matches.is_present(options::TIME) {
need_clockchange = true;
assumptions = false;
}
if matches.is_present(options::USERS) {
need_users = true;
include_idle = true;
assumptions = false;
}
if assumptions {
need_users = true;
short_output = true;
}
if include_exit {
short_output = false;
}
}
let my_line_only = matches.is_present(options::ONLY_HOSTNAME_USER) || files.len() == 2;
let mut who = Who {
do_lookup,

View file

@ -20,7 +20,6 @@ uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(target_os = "windows")'.dependencies]
advapi32-sys = "0.2.0"
winapi = { version = "0.3", features = ["lmcons"] }
[[bin]]

View file

@ -11,7 +11,7 @@ extern crate winapi;
use self::winapi::shared::lmcons;
use self::winapi::shared::minwindef;
use self::winapi::um::winnt;
use self::winapi::um::{winbase, winnt};
use std::io::{Error, Result};
use std::mem;
use uucore::wide::FromWide;
@ -20,7 +20,7 @@ pub unsafe fn get_username() -> Result<String> {
#[allow(deprecated)]
let mut buffer: [winnt::WCHAR; lmcons::UNLEN as usize + 1] = mem::uninitialized();
let mut len = buffer.len() as minwindef::DWORD;
if advapi32::GetUserNameW(buffer.as_mut_ptr(), &mut len) == 0 {
if winbase::GetUserNameW(buffer.as_mut_ptr(), &mut len) == 0 {
return Err(Error::last_os_error());
}
let username = String::from_wide(&buffer[..len as usize - 1]);

View file

@ -44,7 +44,6 @@ entries = ["libc"]
fs = ["libc"]
fsext = ["libc", "time"]
mode = ["libc"]
parse_time = []
perms = ["libc"]
process = ["libc"]
ringbuffer = []

View file

@ -6,8 +6,6 @@ pub mod encoding;
pub mod fs;
#[cfg(feature = "fsext")]
pub mod fsext;
#[cfg(feature = "parse_time")]
pub mod parse_time;
#[cfg(feature = "ringbuffer")]
pub mod ringbuffer;
#[cfg(feature = "zero-copy")]

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