Merge branch 'master' into implement-more

This commit is contained in:
Sylvestre Ledru 2021-05-31 10:17:15 +02:00 committed by GitHub
commit 8c5dcd0765
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
121 changed files with 1389 additions and 1273 deletions

View file

@ -84,7 +84,7 @@ jobs:
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: nightly
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy
@ -94,7 +94,7 @@ jobs:
run: |
# `clippy` testing
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
S=$(cargo clippy ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::warning file=\2,line=\3,col=\4::WARNING: \`cargo clippy\`: \1/p;" -e '}' ; }
S=$(cargo +nightly clippy --all-targets ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::warning file=\2,line=\3,col=\4::WARNING: \`cargo clippy\`: \1/p;" -e '}' ; }
min_version:
name: MinRustV # Minimum supported rust version
@ -614,3 +614,20 @@ jobs:
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
name: codecov-umbrella
fail_ci_if_error: false
spellcheck:
name: Spell Check
runs-on: ${{ matrix.job.os }}
strategy:
matrix:
job:
- { os: ubuntu-latest }
steps:
- uses: actions/checkout@v1
- name: Install/setup prerequisites
shell: bash
run: |
sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g;
- name: Run `cspell`
shell: bash
run: |
cspell --config .vscode/cSpell.json --no-summary --no-progress $( git ls-files | grep "\.\(rs\|md\)" ) | sed "s/\(.*\):\(.*\):\(.*\) - \(.*\)/::warning file=\1,line=\2,col=\3::cspell: \4/" || true

View file

@ -1,8 +1,17 @@
# https://pre-commit.com
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
- repo: local
hooks:
- id: cargo-check
- id: clippy
- id: fmt
- id: rust-linting
name: Rust linting
description: Run cargo fmt on files included in the commit.
entry: cargo +nightly fmt --
pass_filenames: true
types: [file, rust]
language: system
- id: rust-clippy
name: Rust clippy
description: Run cargo clippy on files included in the commit.
entry: cargo +nightly clippy --all-targets --all-features --
pass_filenames: false
types: [file, rust]
language: system

308
Cargo.lock generated
View file

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "Inflector"
version = "0.11.4"
@ -43,12 +45,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "array-init"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6945cc5422176fc5e602e590c2878d2c2acd9a4fe20a4baa7c28022521698ec6"
[[package]]
name = "arrayvec"
version = "0.4.12"
@ -134,15 +130,8 @@ dependencies = [
"lazy_static",
"memchr 2.4.0",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
[[package]]
name = "byte-tools"
version = "0.2.0"
@ -155,15 +144,6 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cast"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57cdfa5d50aad6cb4d44dcab6101a7f79925bd59d82ca42f38a9856a28865374"
dependencies = [
"rustc_version",
]
[[package]]
name = "cc"
version = "1.0.67"
@ -285,7 +265,6 @@ dependencies = [
"uu_expand",
"uu_expr",
"uu_factor",
"uu_factor_benches",
"uu_false",
"uu_fmt",
"uu_fold",
@ -464,42 +443,6 @@ dependencies = [
"unicode-xid 0.0.4",
]
[[package]]
name = "criterion"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
"itertools 0.10.0",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d"
dependencies = [
"cast",
"itertools 0.9.0",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
@ -842,15 +785,6 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.10.0"
@ -860,21 +794,6 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -1035,6 +954,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "num-bigint"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
@ -1106,12 +1036,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "ouroboros"
version = "0.9.3"
@ -1188,15 +1112,6 @@ dependencies = [
"proc-macro-hack",
]
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "pkg-config"
version = "0.3.19"
@ -1213,34 +1128,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "plotters"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590"
[[package]]
name = "plotters-svg"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
@ -1546,21 +1433,6 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
[[package]]
name = "rustc_version"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
dependencies = [
"semver 0.11.0",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "same-file"
version = "1.0.6"
@ -1582,16 +1454,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser 0.7.0",
]
[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser 0.10.2",
"semver-parser",
]
[[package]]
@ -1600,53 +1463,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "semver-parser"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
[[package]]
name = "serde"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
[[package]]
name = "serde_cbor"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha1"
version = "0.6.0"
@ -1863,28 +1679,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "typenum"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-segmentation"
version = "1.7.1"
@ -2171,6 +1971,8 @@ name = "uu_expr"
version = "0.0.6"
dependencies = [
"libc",
"num-bigint",
"num-traits",
"onig",
"uucore",
"uucore_procs",
@ -2180,6 +1982,7 @@ dependencies = [
name = "uu_factor"
version = "0.0.6"
dependencies = [
"clap",
"coz",
"num-traits",
"paste",
@ -2190,17 +1993,6 @@ dependencies = [
"uucore_procs",
]
[[package]]
name = "uu_factor_benches"
version = "0.0.0"
dependencies = [
"array-init",
"criterion",
"rand 0.7.3",
"rand_chacha 0.2.2",
"uu_factor",
]
[[package]]
name = "uu_false"
version = "0.0.6"
@ -2681,7 +2473,7 @@ dependencies = [
"ouroboros",
"rand 0.7.3",
"rayon",
"semver 0.9.0",
"semver",
"tempfile",
"unicode-width",
"uucore",
@ -3029,70 +2821,6 @@ version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote 1.0.9",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4"
dependencies = [
"quote 1.0.9",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f"
[[package]]
name = "web-sys"
version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wild"
version = "2.0.4"

View file

@ -325,7 +325,8 @@ who = { optional=true, version="0.0.6", package="uu_who", path="src/uu/who"
whoami = { optional=true, version="0.0.6", package="uu_whoami", path="src/uu/whoami" }
yes = { optional=true, version="0.0.6", package="uu_yes", path="src/uu/yes" }
factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
#
# * pinned transitive dependencies

View file

@ -21,7 +21,7 @@ if changes are not reflected in the report then run `cargo clean` and run the a
### Using Stable Rust
If you are using stable version of Rust that doesn't enable code coverage instrumentation by default
If you are using stable version of Rust that doesn't enable code coverage instrumentation by default
then add `-Z-Zinstrument-coverage` flag to `RUSTFLAGS` env variable specified above.
@ -36,3 +36,7 @@ To use the provided hook:
2. Run `pre-commit install` while in the repository directory
Your git commits will then automatically be checked. If a check fails, an error message will explain why, and your commit will be canceled. You can then make the suggested changes, and run `git commit ...` again.
### Using Clippy
The `msrv` key in the clippy configuration file `clippy.toml` is used to disable lints pertaining to newer features by specifying the minimum supported Rust version (MSRV). However, this key is only supported on `nightly`. To invoke clippy without errors, use `cargo +nightly clippy`. In order to also check tests and non-default crate features, use `cargo +nightly clippy --all-targets --all-features`.

1
clippy.toml Normal file
View file

@ -0,0 +1 @@
msrv = "1.43.1"

View file

@ -136,7 +136,8 @@ fn basename(fullname: &str, suffix: &str) -> String {
}
}
#[allow(clippy::manual_strip)] // can be replaced with strip_suffix once the minimum rust version is 1.45
// can be replaced with strip_suffix once MSRV is 1.45
#[allow(clippy::manual_strip)]
fn strip_suffix(name: &str, suffix: &str) -> String {
if name == suffix {
return name.to_owned();

View file

@ -669,8 +669,8 @@ impl Options {
}
},
backup: backup_mode,
backup_suffix: backup_suffix,
overwrite: overwrite,
backup_suffix,
overwrite,
no_target_dir,
preserve_attributes,
recursive,
@ -1089,7 +1089,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
}
#[cfg(not(windows))]
#[allow(clippy::unnecessary_wraps)] // needed for windows version
#[allow(clippy::unnecessary_unwrap)] // 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(()),
@ -1108,7 +1108,7 @@ fn context_for(src: &Path, dest: &Path) -> String {
/// Implements a simple backup copy for the destination file.
/// TODO: for the backup, should this function be replaced by `copy_file(...)`?
fn backup_dest(dest: &Path, backup_path: &PathBuf) -> CopyResult<PathBuf> {
fn backup_dest(dest: &Path, backup_path: &Path) -> CopyResult<PathBuf> {
fs::copy(dest, &backup_path)?;
Ok(backup_path.into())
}

View file

@ -124,12 +124,7 @@ where
// split the file based on patterns
for pattern in patterns.into_iter() {
let pattern_as_str = pattern.to_string();
#[allow(clippy::match_like_matches_macro)]
let is_skip = if let patterns::Pattern::SkipToMatch(_, _, _) = pattern {
true
} else {
false
};
let is_skip = matches!(pattern, patterns::Pattern::SkipToMatch(_, _, _));
match pattern {
patterns::Pattern::UpToLine(n, ex) => {
let mut up_to_line = n;
@ -488,10 +483,11 @@ where
/// Shrink the buffer so that its length is equal to the set size, returning an iterator for
/// the elements that were too much.
fn shrink_buffer_to_size(&mut self) -> impl Iterator<Item = String> + '_ {
let mut shrink_offset = 0;
if self.buffer.len() > self.size {
shrink_offset = self.buffer.len() - self.size;
}
let shrink_offset = if self.buffer.len() > self.size {
self.buffer.len() - self.size
} else {
0
};
self.buffer
.drain(..shrink_offset)
.map(|(_, line)| line.unwrap())

View file

@ -16,6 +16,8 @@ path = "src/expr.rs"
[dependencies]
libc = "0.2.42"
num-bigint = "0.4.0"
num-traits = "0.2.14"
onig = "~4.3.2"
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }

View file

@ -12,6 +12,8 @@
// spell-checker:ignore (ToDO) binop binops ints paren prec
use num_bigint::BigInt;
use num_traits::{One, Zero};
use onig::{Regex, RegexOptions, Syntax};
use crate::tokens::Token;
@ -39,20 +41,17 @@ impl AstNode {
for _ in 0..depth {
print!("\t",);
}
match *self {
AstNode::Leaf {
ref token_idx,
ref value,
} => println!(
match self {
AstNode::Leaf { token_idx, value } => println!(
"Leaf( {} ) at #{} ( evaluate -> {:?} )",
value,
token_idx,
self.evaluate()
),
AstNode::Node {
ref token_idx,
ref op_type,
ref operands,
token_idx,
op_type,
operands,
} => {
println!(
"Node( {} ) at #{} (evaluate -> {:?})",
@ -81,36 +80,33 @@ impl AstNode {
})
}
pub fn evaluate(&self) -> Result<String, String> {
match *self {
AstNode::Leaf { ref value, .. } => Ok(value.clone()),
AstNode::Node { ref op_type, .. } => match self.operand_values() {
match self {
AstNode::Leaf { value, .. } => Ok(value.clone()),
AstNode::Node { op_type, .. } => match self.operand_values() {
Err(reason) => Err(reason),
Ok(operand_values) => match op_type.as_ref() {
"+" => infix_operator_two_ints(
|a: i64, b: i64| checked_binop(|| a.checked_add(b), "+"),
&operand_values,
),
"-" => infix_operator_two_ints(
|a: i64, b: i64| checked_binop(|| a.checked_sub(b), "-"),
&operand_values,
),
"*" => infix_operator_two_ints(
|a: i64, b: i64| checked_binop(|| a.checked_mul(b), "*"),
&operand_values,
),
"+" => {
infix_operator_two_ints(|a: BigInt, b: BigInt| Ok(a + b), &operand_values)
}
"-" => {
infix_operator_two_ints(|a: BigInt, b: BigInt| Ok(a - b), &operand_values)
}
"*" => {
infix_operator_two_ints(|a: BigInt, b: BigInt| Ok(a * b), &operand_values)
}
"/" => infix_operator_two_ints(
|a: i64, b: i64| {
if b == 0 {
|a: BigInt, b: BigInt| {
if b.is_zero() {
Err("division by zero".to_owned())
} else {
checked_binop(|| a.checked_div(b), "/")
Ok(a / b)
}
},
&operand_values,
),
"%" => infix_operator_two_ints(
|a: i64, b: i64| {
if b == 0 {
|a: BigInt, b: BigInt| {
if b.is_zero() {
Err("division by zero".to_owned())
} else {
Ok(a % b)
@ -119,32 +115,32 @@ impl AstNode {
&operand_values,
),
"=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a == b)),
|a: BigInt, b: BigInt| Ok(bool_as_int(a == b)),
|a: &String, b: &String| Ok(bool_as_string(a == b)),
&operand_values,
),
"!=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a != b)),
|a: BigInt, b: BigInt| Ok(bool_as_int(a != b)),
|a: &String, b: &String| Ok(bool_as_string(a != b)),
&operand_values,
),
"<" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a < b)),
|a: BigInt, b: BigInt| Ok(bool_as_int(a < b)),
|a: &String, b: &String| Ok(bool_as_string(a < b)),
&operand_values,
),
">" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a > b)),
|a: BigInt, b: BigInt| Ok(bool_as_int(a > b)),
|a: &String, b: &String| Ok(bool_as_string(a > b)),
&operand_values,
),
"<=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a <= b)),
|a: BigInt, b: BigInt| Ok(bool_as_int(a <= b)),
|a: &String, b: &String| Ok(bool_as_string(a <= b)),
&operand_values,
),
">=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a >= b)),
|a: BigInt, b: BigInt| Ok(bool_as_int(a >= b)),
|a: &String, b: &String| Ok(bool_as_string(a >= b)),
&operand_values,
),
@ -161,7 +157,7 @@ impl AstNode {
}
}
pub fn operand_values(&self) -> Result<Vec<String>, String> {
if let AstNode::Node { ref operands, .. } = *self {
if let AstNode::Node { operands, .. } = self {
let mut out = Vec::with_capacity(operands.len());
for operand in operands {
match operand.evaluate() {
@ -217,9 +213,9 @@ fn maybe_dump_ast(result: &Result<Box<AstNode>, String>) {
if let Ok(debug_var) = env::var("EXPR_DEBUG_AST") {
if debug_var == "1" {
println!("EXPR_DEBUG_AST");
match *result {
Ok(ref ast) => ast.debug_dump(),
Err(ref reason) => println!("\terr: {:?}", reason),
match result {
Ok(ast) => ast.debug_dump(),
Err(reason) => println!("\terr: {:?}", reason),
}
}
}
@ -304,7 +300,7 @@ fn push_token_to_either_stack(
out_stack: &mut TokenStack,
op_stack: &mut TokenStack,
) -> Result<(), String> {
let result = match *token {
let result = match token {
Token::Value { .. } => {
out_stack.push((token_idx, token.clone()));
Ok(())
@ -420,24 +416,14 @@ fn move_till_match_paren(
}
}
fn checked_binop<F: Fn() -> Option<T>, T>(cb: F, op: &str) -> Result<T, String> {
match cb() {
Some(v) => Ok(v),
None => Err(format!("{}: Numerical result out of range", op)),
}
}
fn infix_operator_two_ints<F>(f: F, values: &[String]) -> Result<String, String>
where
F: Fn(i64, i64) -> Result<i64, String>,
F: Fn(BigInt, BigInt) -> Result<BigInt, String>,
{
assert!(values.len() == 2);
if let Ok(left) = values[0].parse::<i64>() {
if let Ok(right) = values[1].parse::<i64>() {
return match f(left, right) {
Ok(result) => Ok(result.to_string()),
Err(reason) => Err(reason),
};
if let Ok(left) = values[0].parse::<BigInt>() {
if let Ok(right) = values[1].parse::<BigInt>() {
return f(left, right).map(|big_int| big_int.to_string());
}
}
Err("Expected an integer operand".to_string())
@ -449,13 +435,14 @@ fn infix_operator_two_ints_or_two_strings<FI, FS>(
values: &[String],
) -> Result<String, String>
where
FI: Fn(i64, i64) -> Result<i64, String>,
FI: Fn(BigInt, BigInt) -> Result<u8, String>,
FS: Fn(&String, &String) -> Result<String, String>,
{
assert!(values.len() == 2);
if let (Some(a_int), Some(b_int)) =
(values[0].parse::<i64>().ok(), values[1].parse::<i64>().ok())
{
if let (Some(a_int), Some(b_int)) = (
values[0].parse::<BigInt>().ok(),
values[1].parse::<BigInt>().ok(),
) {
match fi(a_int, b_int) {
Ok(result) => Ok(result.to_string()),
Err(reason) => Err(reason),
@ -541,7 +528,7 @@ fn prefix_operator_substr(values: &[String]) -> String {
subj.chars().skip(idx).take(len).collect()
}
fn bool_as_int(b: bool) -> i64 {
fn bool_as_int(b: bool) -> u8 {
if b {
1
} else {
@ -559,8 +546,8 @@ fn value_as_bool(s: &str) -> bool {
if s.is_empty() {
return false;
}
match s.parse::<i64>() {
Ok(n) => n != 0,
match s.parse::<BigInt>() {
Ok(n) => n.is_one(),
Err(_) => true,
}
}

View file

@ -18,6 +18,8 @@
// spell-checker:ignore (ToDO) paren
use num_bigint::BigInt;
#[derive(Debug, Clone)]
pub enum Token {
Value {
@ -51,24 +53,19 @@ impl Token {
}
fn is_infix_plus(&self) -> bool {
match *self {
Token::InfixOp { ref value, .. } => value == "+",
match self {
Token::InfixOp { value, .. } => value == "+",
_ => false,
}
}
fn is_a_number(&self) -> bool {
match *self {
Token::Value { ref value, .. } => value.parse::<i64>().is_ok(),
match self {
Token::Value { value, .. } => value.parse::<BigInt>().is_ok(),
_ => false,
}
}
fn is_a_close_paren(&self) -> bool {
#[allow(clippy::match_like_matches_macro)]
// `matches!(...)` macro not stabilized until rust v1.42
match *self {
Token::ParClose => true,
_ => false,
}
matches!(*self, Token::ParClose)
}
}
@ -147,7 +144,7 @@ fn push_token_if_not_escaped(acc: &mut Vec<(usize, Token)>, tok_idx: usize, toke
// Smells heuristics... :(
let prev_is_plus = match acc.last() {
None => false,
Some(ref t) => t.1.is_infix_plus(),
Some(t) => t.1.is_infix_plus(),
};
let should_use_as_escaped = if prev_is_plus && acc.len() >= 2 {
let pre_prev = &acc[acc.len() - 2];

View file

@ -21,6 +21,7 @@ rand = { version = "0.7", features = ["small_rng"] }
smallvec = { version = "0.6.14, < 1.0" }
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" }
uucore_procs = { version = ">=0.0.5", package = "uucore_procs", path = "../../uucore_procs" }
clap = "2.33"
[dev-dependencies]
paste = "0.1.18"

View file

@ -30,7 +30,7 @@ impl Iterator for Sieve {
#[inline]
fn next(&mut self) -> Option<u64> {
while let Some(n) = self.inner.next() {
for n in &mut self.inner {
let mut prime = true;
while let Some((next, inc)) = self.filts.peek() {
// need to keep checking the min element of the heap

View file

@ -13,18 +13,21 @@ use std::error::Error;
use std::io::{self, stdin, stdout, BufRead, Write};
mod factor;
use clap::{App, Arg};
pub use factor::*;
use uucore::InvalidEncodingHandling;
mod miller_rabin;
pub mod numeric;
mod rho;
pub mod table;
static SYNTAX: &str = "[OPTION] [NUMBER]...";
static SUMMARY: &str = "Print the prime factors of the given number(s).
If none are specified, read from standard input.";
static LONG_HELP: &str = "";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static SUMMARY: &str = "Print the prime factors of the given NUMBER(s).
If none are specified, read from standard input.";
mod options {
pub static NUMBER: &str = "NUMBER";
}
fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box<dyn Error>> {
num_str
@ -34,14 +37,21 @@ fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box<dy
}
pub fn uumain(args: impl uucore::Args) -> i32 {
let matches = app!(SYNTAX, SUMMARY, LONG_HELP).parse(
args.collect_str(InvalidEncodingHandling::Ignore)
.accept_any(),
);
let matches = App::new(executable!())
.version(VERSION)
.about(SUMMARY)
.arg(Arg::with_name(options::NUMBER).multiple(true))
.get_matches_from(args);
let stdout = stdout();
let mut w = io::BufWriter::new(stdout.lock());
if matches.free.is_empty() {
if let Some(values) = matches.values_of(options::NUMBER) {
for number in values {
if let Err(e) = print_factors_str(number, &mut w) {
show_warning!("{}: {}", number, e);
}
}
} else {
let stdin = stdin();
for line in stdin.lock().lines() {
@ -51,12 +61,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
}
}
} else {
for number in &matches.free {
if let Err(e) = print_factors_str(number, &mut w) {
show_warning!("{}: {}", number, e);
}
}
}
if let Err(e) = w.flush() {

View file

@ -264,12 +264,9 @@ impl<'a> ParagraphStream<'a> {
return false;
}
#[allow(clippy::match_like_matches_macro)]
// `matches!(...)` macro not stabilized until rust v1.42
l_slice[..colon_posn].chars().all(|x| match x as usize {
y if !(33..=126).contains(&y) => false,
_ => true,
})
l_slice[..colon_posn]
.chars()
.all(|x| !matches!(x as usize, y if !(33..=126).contains(&y)))
}
}
}
@ -541,12 +538,7 @@ impl<'a> WordSplit<'a> {
}
fn is_punctuation(c: char) -> bool {
#[allow(clippy::match_like_matches_macro)]
// `matches!(...)` macro not stabilized until rust v1.42
match c {
'!' | '.' | '?' => true,
_ => false,
}
matches!(c, '!' | '.' | '?')
}
}

View file

@ -51,14 +51,23 @@ struct Options {
}
fn is_custom_binary(program: &str) -> bool {
#[allow(clippy::match_like_matches_macro)]
// `matches!(...)` macro not stabilized until rust v1.42
match program {
"md5sum" | "sha1sum" | "sha224sum" | "sha256sum" | "sha384sum" | "sha512sum"
| "sha3sum" | "sha3-224sum" | "sha3-256sum" | "sha3-384sum" | "sha3-512sum"
| "shake128sum" | "shake256sum" | "b2sum" => true,
_ => false,
}
matches!(
program,
"md5sum"
| "sha1sum"
| "sha224sum"
| "sha256sum"
| "sha384sum"
| "sha512sum"
| "sha3sum"
| "sha3-224sum"
| "sha3-256sum"
| "sha3-384sum"
| "sha3-512sum"
| "shake128sum"
| "shake256sum"
| "b2sum"
)
}
#[allow(clippy::cognitive_complexity)]

View file

@ -14,7 +14,7 @@ pub fn parse_obsolete(src: &str) -> Option<Result<impl Iterator<Item = OsString>
let mut num_end = 0usize;
let mut has_num = false;
let mut last_char = 0 as char;
while let Some((n, c)) = chars.next() {
for (n, c) in &mut chars {
if c.is_numeric() {
has_num = true;
num_end = n;
@ -109,7 +109,7 @@ pub fn parse_num(src: &str) -> Result<(usize, bool), ParseError> {
let mut num_end = 0usize;
let mut last_char = 0 as char;
let mut num_count = 0usize;
while let Some((n, c)) = chars.next() {
for (n, c) in &mut chars {
if c.is_numeric() {
num_end = n;
num_count += 1;

View file

@ -520,6 +520,7 @@ fn copy_file_to_file(file: &Path, target: &Path, b: &Behavior) -> i32 {
///
/// If the copy system call fails, we print a verbose error and return an empty error value.
///
#[allow(clippy::cognitive_complexity)]
fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
if b.compare && !need_copy(from, to, b) {
return Ok(());

View file

@ -218,6 +218,7 @@ struct LongFormat {
}
impl Config {
#[allow(clippy::cognitive_complexity)]
fn from(options: clap::ArgMatches) -> Config {
let (mut format, opt) = if let Some(format_) = options.value_of(options::FORMAT) {
(
@ -1222,7 +1223,7 @@ fn list(locs: Vec<String>, config: Config) -> i32 {
sort_entries(&mut dirs, &config);
for dir in dirs {
if locs.len() > 1 {
if locs.len() > 1 || config.recursive {
let _ = writeln!(out, "\n{}:", dir.p_buf.display());
}
enter_directory(&dir, &config, &mut out);
@ -1614,7 +1615,7 @@ fn display_date(metadata: &Metadata, config: &Config) -> String {
Some(time) => {
//Date is recent if from past 6 months
//According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
let recent = time + chrono::Duration::seconds(31556952 / 2) > chrono::Local::now();
let recent = time + chrono::Duration::seconds(31_556_952 / 2) > chrono::Local::now();
match config.time_style {
TimeStyle::FullIso => time.format("%Y-%m-%d %H:%M:%S.%f %z"),
@ -1696,7 +1697,6 @@ fn file_is_executable(md: &Metadata) -> bool {
md.mode() & ((S_IXUSR | S_IXGRP | S_IXOTH) as u32) != 0
}
#[allow(clippy::clippy::collapsible_else_if)]
fn classify_file(path: &PathData) -> Option<char> {
let file_type = path.file_type()?;

View file

@ -210,21 +210,14 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) ->
0
}
fn exec(
dir: PathBuf,
prefix: &str,
rand: usize,
suffix: &str,
make_dir: bool,
quiet: bool,
) -> i32 {
fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool, quiet: bool) -> i32 {
let res = if make_dir {
let tmpdir = Builder::new()
.prefix(prefix)
.rand_bytes(rand)
.suffix(suffix)
.tempdir_in(&dir);
// `into_path` consumes the TempDir without removing it
tmpdir.map(|d| d.into_path().to_string_lossy().to_string())
} else {
@ -233,7 +226,7 @@ fn exec(
.rand_bytes(rand)
.suffix(suffix)
.tempfile_in(&dir);
match tmpfile {
Ok(f) => {
// `keep` ensures that the file is not deleted
@ -245,7 +238,7 @@ fn exec(
}
}
}
Err(x) => Err(x)
Err(x) => Err(x),
}
};

View file

@ -85,12 +85,7 @@ fn od_format_type(type_char: FormatType, byte_size: u8) -> Option<FormatterItemI
}
fn od_argument_with_option(ch: char) -> bool {
#[allow(clippy::match_like_matches_macro)]
// `matches!(...)` macro not stabilized until rust v1.42
match ch {
'A' | 'j' | 'N' | 'S' | 'w' => true,
_ => false,
}
matches!(ch, 'A' | 'j' | 'N' | 'S' | 'w')
}
/// Parses format flags from command line

View file

@ -76,7 +76,7 @@ pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs,
input_strings.push("-");
}
Ok(CommandLineInputs::FileNames(
input_strings.iter().map(|s| s.to_string()).collect(),
input_strings.iter().map(|&s| s.to_string()).collect(),
))
}
@ -92,7 +92,7 @@ pub fn parse_inputs_traditional(input_strings: Vec<&str>) -> Result<CommandLineI
Ok(match offset0 {
Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)),
_ => CommandLineInputs::FileNames(
input_strings.iter().map(|s| s.to_string()).collect(),
input_strings.iter().map(|&s| s.to_string()).collect(),
),
})
}
@ -179,7 +179,7 @@ mod tests {
impl<'a> MockOptions<'a> {
fn new(inputs: Vec<&'a str>, option_names: Vec<&'a str>) -> MockOptions<'a> {
MockOptions {
inputs: inputs.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
inputs: inputs.iter().map(|&s| s.to_string()).collect::<Vec<_>>(),
option_names,
}
}

View file

@ -50,10 +50,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
let mut separator = "\n";
if matches.is_present(OPT_NULL) {
separator = "\x00";
}
let separator = if matches.is_present(OPT_NULL) {
"\x00"
} else {
"\n"
};
if variables.is_empty() {
for (env_var, value) in env::vars() {

View file

@ -108,10 +108,13 @@ impl WordFilter {
// Ignore empty string regex from cmd-line-args
let arg_reg: Option<String> = if matches.is_present(options::WORD_REGEXP) {
match matches.value_of(options::WORD_REGEXP) {
Some(v) => match v.is_empty() {
true => None,
false => Some(v.to_string()),
},
Some(v) => {
if v.is_empty() {
None
} else {
Some(v.to_string())
}
}
None => None,
}
} else {

View file

@ -386,13 +386,8 @@ fn prompt(msg: &str) -> bool {
let stdin = stdin();
let mut stdin = stdin.lock();
#[allow(clippy::match_like_matches_macro)]
// `matches!(...)` macro not stabilized until rust v1.42
match stdin.read_until(b'\n', &mut buf) {
Ok(x) if x > 0 => match buf[0] {
b'y' | b'Y' => true,
_ => false,
},
Ok(x) if x > 0 => matches!(buf[0], b'y' | b'Y'),
_ => false,
}
}

View file

@ -2,7 +2,7 @@
Most of the time when sorting is spent comparing lines. The comparison functions however differ based
on which arguments are passed to `sort`, therefore it is important to always benchmark multiple scenarios.
This is an overwiew over what was benchmarked, and if you make changes to `sort`, you are encouraged to check
This is an overview over what was benchmarked, and if you make changes to `sort`, you are encouraged to check
how performance was affected for the workloads listed below. Feel free to add other workloads to the
list that we should improve / make sure not to regress.

View file

@ -26,7 +26,7 @@ use std::{
///
/// The code we should exit with.
pub fn check(path: &str, settings: &GlobalSettings) -> i32 {
let file = open(path).expect("failed to open input file");
let file = open(path);
let (recycled_sender, recycled_receiver) = sync_channel(2);
let (loaded_sender, loaded_receiver) = sync_channel(2);
thread::spawn({

View file

@ -73,6 +73,7 @@ impl Chunk {
/// * `lines`: The recycled vector to fill with lines. Must be empty.
/// * `settings`: The global settings.
#[allow(clippy::too_many_arguments)]
#[allow(clippy::borrowed_box)]
pub fn read(
sender_option: &mut Option<SyncSender<Chunk>>,
mut buffer: Vec<u8>,
@ -164,6 +165,7 @@ fn parse_lines<'a>(
/// The remaining bytes must be copied to the start of the buffer for the next invocation,
/// if another invocation is necessary, which is determined by the other return value.
/// * Whether this function should be called again.
#[allow(clippy::borrowed_box)]
fn read_to_buffer(
file: &mut Box<dyn Read + Send>,
next_files: &mut impl Iterator<Item = Box<dyn Read + Send>>,

View file

@ -34,7 +34,12 @@ const MIN_BUFFER_SIZE: usize = 8_000;
/// Sort files by using auxiliary files for storing intermediate chunks (if needed), and output the result.
pub fn ext_sort(files: &mut impl Iterator<Item = Box<dyn Read + Send>>, settings: &GlobalSettings) {
let tmp_dir = crash_if_err!(1, tempfile::Builder::new().prefix("uutils_sort").tempdir_in(&settings.tmp_dir));
let tmp_dir = crash_if_err!(
1,
tempfile::Builder::new()
.prefix("uutils_sort")
.tempdir_in(&settings.tmp_dir)
);
let (sorted_sender, sorted_receiver) = std::sync::mpsc::sync_channel(1);
let (recycled_sender, recycled_receiver) = std::sync::mpsc::sync_channel(1);
thread::spawn({

View file

@ -29,7 +29,7 @@ pub fn merge<'a>(files: &[impl AsRef<OsStr>], settings: &'a GlobalSettings) -> F
let (request_sender, request_receiver) = channel();
let mut reader_files = Vec::with_capacity(files.len());
let mut loaded_receivers = Vec::with_capacity(files.len());
for (file_number, file) in files.iter().filter_map(open).enumerate() {
for (file_number, file) in files.iter().map(open).enumerate() {
let (sender, receiver) = sync_channel(2);
loaded_receivers.push(receiver);
reader_files.push(ReaderFile {

View file

@ -68,10 +68,10 @@ impl NumInfo {
}
first_char = false;
if parse_settings
.thousands_separator
.map_or(false, |c| c == char)
{
if matches!(
parse_settings.thousands_separator,
Some(c) if c == char
) {
continue;
}
@ -174,7 +174,11 @@ impl NumInfo {
pub fn numeric_str_cmp((a, a_info): (&str, &NumInfo), (b, b_info): (&str, &NumInfo)) -> Ordering {
// check for a difference in the sign
if a_info.sign != b_info.sign {
return a_info.sign.cmp(&b_info.sign);
return if a.is_empty() && b.is_empty() {
Ordering::Equal
} else {
a_info.sign.cmp(&b_info.sign)
};
}
// check for a difference in the exponent
@ -419,8 +423,8 @@ mod tests {
#[test]
fn minus_zero() {
// This matches GNU sort behavior.
test_helper("-0", "0", Ordering::Less);
test_helper("-0x", "0", Ordering::Less);
test_helper("-0", "0", Ordering::Equal);
test_helper("-0x", "0", Ordering::Equal);
}
#[test]
fn double_minus() {

View file

@ -99,17 +99,16 @@ static OPT_TMP_DIR: &str = "temporary-directory";
static ARG_FILES: &str = "files";
static DECIMAL_PT: char = '.';
static THOUSANDS_SEP: char = ',';
static NEGATIVE: char = '-';
static POSITIVE: char = '+';
const NEGATIVE: char = '-';
const POSITIVE: char = '+';
// Choosing a higher buffer size does not result in performance improvements
// (at least not on my machine). TODO: In the future, we should also take the amount of
// available memory into consideration, instead of relying on this constant only.
static DEFAULT_BUF_SIZE: usize = 1_000_000_000; // 1 GB
#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy)]
#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Copy, Debug)]
enum SortMode {
Numeric,
HumanNumeric,
@ -119,6 +118,21 @@ enum SortMode {
Random,
Default,
}
impl SortMode {
fn get_short_name(&self) -> Option<char> {
match self {
SortMode::Numeric => Some('n'),
SortMode::HumanNumeric => Some('h'),
SortMode::GeneralNumeric => Some('g'),
SortMode::Month => Some('M'),
SortMode::Version => Some('V'),
SortMode::Random => Some('R'),
SortMode::Default => None,
}
}
}
#[derive(Clone)]
pub struct GlobalSettings {
mode: SortMode,
@ -212,7 +226,7 @@ impl Default for GlobalSettings {
}
}
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Debug)]
struct KeySettings {
mode: SortMode,
ignore_blanks: bool,
@ -222,6 +236,60 @@ struct KeySettings {
reverse: bool,
}
impl KeySettings {
/// Checks if the supplied combination of `mode`, `ignore_non_printing` and `dictionary_order` is allowed.
fn check_compatibility(
mode: SortMode,
ignore_non_printing: bool,
dictionary_order: bool,
) -> Result<(), String> {
if matches!(
mode,
SortMode::Numeric | SortMode::HumanNumeric | SortMode::GeneralNumeric | SortMode::Month
) {
if dictionary_order {
return Err(format!(
"options '-{}{}' are incompatible",
'd',
mode.get_short_name().unwrap()
));
} else if ignore_non_printing {
return Err(format!(
"options '-{}{}' are incompatible",
'i',
mode.get_short_name().unwrap()
));
}
}
Ok(())
}
fn set_sort_mode(&mut self, mode: SortMode) -> Result<(), String> {
if self.mode != SortMode::Default {
return Err(format!(
"options '-{}{}' are incompatible",
self.mode.get_short_name().unwrap(),
mode.get_short_name().unwrap()
));
}
Self::check_compatibility(mode, self.ignore_non_printing, self.dictionary_order)?;
self.mode = mode;
Ok(())
}
fn set_dictionary_order(&mut self) -> Result<(), String> {
Self::check_compatibility(self.mode, self.ignore_non_printing, true)?;
self.dictionary_order = true;
Ok(())
}
fn set_ignore_non_printing(&mut self) -> Result<(), String> {
Self::check_compatibility(self.mode, true, self.dictionary_order)?;
self.ignore_non_printing = true;
Ok(())
}
}
impl From<&GlobalSettings> for KeySettings {
fn from(settings: &GlobalSettings) -> Self {
Self {
@ -235,6 +303,12 @@ impl From<&GlobalSettings> for KeySettings {
}
}
impl Default for KeySettings {
fn default() -> Self {
Self::from(&GlobalSettings::default())
}
}
#[derive(Clone, Debug)]
enum NumCache {
AsF64(GeneralF64ParseResult),
@ -297,10 +371,10 @@ impl<'a> Line<'a> {
fn print(&self, writer: &mut impl Write, settings: &GlobalSettings) {
if settings.zero_terminated && !settings.debug {
crash_if_err!(1, writer.write_all(self.line.as_bytes()));
crash_if_err!(1, writer.write_all("\0".as_bytes()));
crash_if_err!(1, writer.write_all(b"\0"));
} else if !settings.debug {
crash_if_err!(1, writer.write_all(self.line.as_bytes()));
crash_if_err!(1, writer.write_all("\n".as_bytes()));
crash_if_err!(1, writer.write_all(b"\n"));
} else {
crash_if_err!(1, self.print_debug(settings, writer));
}
@ -330,8 +404,7 @@ impl<'a> Line<'a> {
&self.line[selection.clone()],
NumInfoParseSettings {
accept_si_units: selector.settings.mode == SortMode::HumanNumeric,
thousands_separator: Some(THOUSANDS_SEP),
decimal_pt: Some(DECIMAL_PT),
..Default::default()
},
);
let initial_selection = selection.clone();
@ -367,16 +440,24 @@ impl<'a> Line<'a> {
SortMode::Month => {
let initial_selection = &self.line[selection.clone()];
let mut month_chars = initial_selection
.char_indices()
.skip_while(|(_, c)| c.is_whitespace());
let month = if month_parse(initial_selection) == Month::Unknown {
// We failed to parse a month, which is equivalent to matching nothing.
0..0
// Add the "no match for key" marker to the first non-whitespace character.
let first_non_whitespace = month_chars.next();
first_non_whitespace.map_or(
initial_selection.len()..initial_selection.len(),
|(idx, _)| idx..idx,
)
} else {
// We parsed a month. Match the three first non-whitespace characters, which must be the month we parsed.
let mut chars = initial_selection
.char_indices()
.skip_while(|(_, c)| c.is_whitespace());
chars.next().unwrap().0
..chars.nth(2).map_or(initial_selection.len(), |(idx, _)| idx)
// We parsed a month. Match the first three non-whitespace characters, which must be the month we parsed.
month_chars.next().unwrap().0
..month_chars
.nth(2)
.map_or(initial_selection.len(), |(idx, _)| idx)
};
// Shorten selection to month.
@ -406,14 +487,18 @@ impl<'a> Line<'a> {
}
}
}
if !(settings.mode == SortMode::Random
|| settings.stable
|| settings.unique
|| !(settings.dictionary_order
if settings.mode != SortMode::Random
&& !settings.stable
&& !settings.unique
&& (settings.dictionary_order
|| settings.ignore_blanks
|| settings.ignore_case
|| settings.ignore_non_printing
|| settings.mode != SortMode::Default))
|| settings.mode != SortMode::Default
|| settings
.selectors
.last()
.map_or(true, |selector| selector != &Default::default()))
{
// A last resort comparator is in use, underline the whole line.
if self.line.is_empty() {
@ -477,7 +562,7 @@ fn tokenize_with_separator(line: &str, separator: char) -> Vec<Field> {
tokens
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Debug)]
struct KeyPosition {
/// 1-indexed, 0 is invalid.
field: usize,
@ -487,87 +572,45 @@ struct KeyPosition {
}
impl KeyPosition {
fn parse(key: &str, default_char_index: usize, settings: &mut KeySettings) -> Self {
fn new(key: &str, default_char_index: usize, ignore_blanks: bool) -> Result<Self, String> {
let mut field_and_char = key.split('.');
let mut field = field_and_char
let field = field_and_char
.next()
.unwrap_or_else(|| crash!(1, "invalid key `{}`", key));
let mut char = field_and_char.next();
// If there is a char index, we expect options to appear after it. Otherwise we expect them after the field index.
let value_with_options = char.as_mut().unwrap_or(&mut field);
let mut ignore_blanks = settings.ignore_blanks;
if let Some(options_start) = value_with_options.chars().position(char::is_alphabetic) {
for option in value_with_options[options_start..].chars() {
// valid options: MbdfghinRrV
match option {
'M' => settings.mode = SortMode::Month,
'b' => ignore_blanks = true,
'd' => settings.dictionary_order = true,
'f' => settings.ignore_case = true,
'g' => settings.mode = SortMode::GeneralNumeric,
'h' => settings.mode = SortMode::HumanNumeric,
'i' => settings.ignore_non_printing = true,
'n' => settings.mode = SortMode::Numeric,
'R' => settings.mode = SortMode::Random,
'r' => settings.reverse = true,
'V' => settings.mode = SortMode::Version,
c => crash!(1, "invalid option for key: `{}`", c),
}
// All numeric sorts and month sort conflict with dictionary_order and ignore_non_printing.
// Instad of reporting an error, let them overwrite each other.
// FIXME: This should only override if the overridden flag is a global flag.
// If conflicting flags are attached to the key, GNU sort crashes and we should probably too.
match option {
'h' | 'n' | 'g' | 'M' => {
settings.dictionary_order = false;
settings.ignore_non_printing = false;
}
'd' | 'i' => {
settings.mode = match settings.mode {
SortMode::Numeric
| SortMode::HumanNumeric
| SortMode::GeneralNumeric
| SortMode::Month => SortMode::Default,
// Only SortMode::Default and SortMode::Version work with dictionary_order and ignore_non_printing
m @ SortMode::Default
| m @ SortMode::Version
| m @ SortMode::Random => m,
}
}
_ => {}
}
}
// Strip away option characters from the original value so we can parse it later
*value_with_options = &value_with_options[..options_start];
}
.ok_or_else(|| format!("invalid key `{}`", key))?;
let char = field_and_char.next();
let field = field
.parse()
.unwrap_or_else(|e| crash!(1, "failed to parse field index for key `{}`: {}", key, e));
.map_err(|e| format!("failed to parse field index `{}`: {}", field, e))?;
if field == 0 {
crash!(1, "field index was 0");
return Err("field index can not be 0".to_string());
}
let char = char.map_or(default_char_index, |char| {
char.parse().unwrap_or_else(|e| {
crash!(
1,
"failed to parse character index for key `{}`: {}",
key,
e
)
})
});
Self {
let char = char.map_or(Ok(default_char_index), |char| {
char.parse()
.map_err(|e| format!("failed to parse character index `{}`: {}", char, e))
})?;
Ok(Self {
field,
char,
ignore_blanks,
})
}
}
impl Default for KeyPosition {
fn default() -> Self {
KeyPosition {
field: 1,
char: 1,
ignore_blanks: false,
}
}
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Debug)]
struct FieldSelector {
from: KeyPosition,
to: Option<KeyPosition>,
@ -577,21 +620,120 @@ struct FieldSelector {
is_default_selection: bool,
}
impl FieldSelector {
fn new(from: KeyPosition, to: Option<KeyPosition>, settings: KeySettings) -> Self {
impl Default for FieldSelector {
fn default() -> Self {
Self {
is_default_selection: from.field == 1
&& from.char == 1
&& to.is_none()
// TODO: Once our MinRustV is 1.42 or higher, change this to the matches! macro
&& match settings.mode {
SortMode::Numeric | SortMode::GeneralNumeric | SortMode::HumanNumeric => false,
_ => true,
},
needs_tokens: from.field != 1 || from.char == 0 || to.is_some(),
from,
to,
settings,
from: Default::default(),
to: None,
settings: Default::default(),
needs_tokens: false,
is_default_selection: true,
}
}
}
impl FieldSelector {
/// Splits this position into the actual position and the attached options.
fn split_key_options(position: &str) -> (&str, &str) {
if let Some((options_start, _)) = position.char_indices().find(|(_, c)| c.is_alphabetic()) {
position.split_at(options_start)
} else {
(position, "")
}
}
fn parse(key: &str, global_settings: &GlobalSettings) -> Self {
let mut from_to = key.split(',');
let (from, from_options) = Self::split_key_options(from_to.next().unwrap());
let to = from_to.next().map(|to| Self::split_key_options(to));
let options_are_empty = from_options.is_empty() && matches!(to, None | Some((_, "")));
crash_if_err!(
2,
if options_are_empty {
// Inherit the global settings if there are no options attached to this key.
(|| {
// This would be ideal for a try block, I think. In the meantime this closure allows
// to use the `?` operator here.
Self::new(
KeyPosition::new(from, 1, global_settings.ignore_blanks)?,
to.map(|(to, _)| KeyPosition::new(to, 0, global_settings.ignore_blanks))
.transpose()?,
KeySettings::from(global_settings),
)
})()
} else {
// Do not inherit from `global_settings`, as there are options attached to this key.
Self::parse_with_options((from, from_options), to)
}
.map_err(|e| format!("failed to parse key `{}`: {}", key, e))
)
}
fn parse_with_options(
(from, from_options): (&str, &str),
to: Option<(&str, &str)>,
) -> Result<Self, String> {
/// Applies `options` to `key_settings`, returning if the 'b'-flag (ignore blanks) was present.
fn parse_key_settings(
options: &str,
key_settings: &mut KeySettings,
) -> Result<bool, String> {
let mut ignore_blanks = false;
for option in options.chars() {
match option {
'M' => key_settings.set_sort_mode(SortMode::Month)?,
'b' => ignore_blanks = true,
'd' => key_settings.set_dictionary_order()?,
'f' => key_settings.ignore_case = true,
'g' => key_settings.set_sort_mode(SortMode::GeneralNumeric)?,
'h' => key_settings.set_sort_mode(SortMode::HumanNumeric)?,
'i' => key_settings.set_ignore_non_printing()?,
'n' => key_settings.set_sort_mode(SortMode::Numeric)?,
'R' => key_settings.set_sort_mode(SortMode::Random)?,
'r' => key_settings.reverse = true,
'V' => key_settings.set_sort_mode(SortMode::Version)?,
c => return Err(format!("invalid option: `{}`", c)),
}
}
Ok(ignore_blanks)
}
let mut key_settings = KeySettings::default();
let from = parse_key_settings(from_options, &mut key_settings)
.map(|ignore_blanks| KeyPosition::new(from, 1, ignore_blanks))??;
let to = if let Some((to, to_options)) = to {
Some(
parse_key_settings(to_options, &mut key_settings)
.map(|ignore_blanks| KeyPosition::new(to, 0, ignore_blanks))??,
)
} else {
None
};
Self::new(from, to, key_settings)
}
fn new(
from: KeyPosition,
to: Option<KeyPosition>,
settings: KeySettings,
) -> Result<Self, String> {
if from.char == 0 {
Err("invalid character index 0 for the start position of a field".to_string())
} else {
Ok(Self {
is_default_selection: from.field == 1
&& from.char == 1
&& to.is_none()
&& !matches!(
settings.mode,
SortMode::Numeric | SortMode::GeneralNumeric | SortMode::HumanNumeric
)
&& !from.ignore_blanks,
needs_tokens: from.field != 1 || from.char == 0 || to.is_some(),
from,
to,
settings,
})
}
}
@ -607,8 +749,7 @@ impl FieldSelector {
range,
NumInfoParseSettings {
accept_si_units: self.settings.mode == SortMode::HumanNumeric,
thousands_separator: Some(THOUSANDS_SEP),
decimal_pt: Some(DECIMAL_PT),
..Default::default()
},
);
// Shorten the range to what we need to pass to numeric_str_cmp later.
@ -650,7 +791,7 @@ impl FieldSelector {
tokens: Option<&[Field]>,
position: &KeyPosition,
) -> Resolution {
if tokens.map_or(false, |fields| fields.len() < position.field) {
if matches!(tokens, Some(tokens) if tokens.len() < position.field) {
Resolution::TooHigh
} else if position.char == 0 {
let end = tokens.unwrap()[position.field - 1].end;
@ -667,22 +808,21 @@ impl FieldSelector {
} else {
tokens.unwrap()[position.field - 1].start
};
// strip blanks if needed
if position.ignore_blanks {
idx += line[idx..]
.char_indices()
.find(|(_, c)| !c.is_whitespace())
.map_or(line[idx..].len(), |(idx, _)| idx);
}
// apply the character index
idx += line[idx..]
.char_indices()
.nth(position.char - 1)
.map_or(line.len(), |(idx, _)| idx);
.map_or(line[idx..].len(), |(idx, _)| idx);
if idx >= line.len() {
Resolution::TooHigh
} else {
if position.ignore_blanks {
if let Some((not_whitespace, _)) =
line[idx..].char_indices().find(|(_, c)| !c.is_whitespace())
{
idx += not_whitespace;
} else {
return Resolution::TooHigh;
}
}
Resolution::StartOfChar(idx)
}
}
@ -692,8 +832,9 @@ impl FieldSelector {
Resolution::StartOfChar(from) => {
let to = self.to.as_ref().map(|to| resolve_index(line, tokens, &to));
match to {
let mut range = match to {
Some(Resolution::StartOfChar(mut to)) => {
// We need to include the character at `to`.
to += line[to..].chars().next().map_or(1, |c| c.len_utf8());
from..to
}
@ -704,7 +845,11 @@ impl FieldSelector {
// If `to` is before the start of the line, report no match.
// This can happen if the line starts with a separator.
Some(Resolution::TooLow) => 0..0,
};
if range.start > range.end {
range.end = range.start;
}
range
}
Resolution::TooLow | Resolution::EndOfChar(_) => {
unreachable!("This should only happen if the field start index is 0, but that should already have caused an error.")
@ -892,7 +1037,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.long(OPT_SEPARATOR)
.help("custom separator for -k")
.takes_value(true))
.arg(Arg::with_name(OPT_ZERO_TERMINATED)
.arg(
Arg::with_name(OPT_ZERO_TERMINATED)
.short("z")
.long(OPT_ZERO_TERMINATED)
.help("line delimiter is NUL, not newline"),
@ -947,7 +1093,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let mut files = Vec::new();
for path in &files0_from {
let reader = open(path.as_str()).expect("Could not read from file specified.");
let reader = open(path.as_str());
let buf_reader = BufReader::new(reader);
for line in buf_reader.split(b'\0').flatten() {
files.push(
@ -1045,43 +1191,27 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if matches.is_present(OPT_KEY) {
for key in &matches.args[OPT_KEY].vals {
let key = key.to_string_lossy();
let mut from_to = key.split(',');
let mut key_settings = KeySettings::from(&settings);
let from = KeyPosition::parse(
from_to
.next()
.unwrap_or_else(|| crash!(1, "invalid key `{}`", key)),
1,
&mut key_settings,
);
if from.char == 0 {
crash!(
1,
"invalid character index 0 in `{}` for the start position of a field",
key
)
}
let to = from_to
.next()
.map(|to| KeyPosition::parse(to, 0, &mut key_settings));
let field_selector = FieldSelector::new(from, to, key_settings);
settings.selectors.push(field_selector);
settings
.selectors
.push(FieldSelector::parse(&key.to_string_lossy(), &settings));
}
}
if !settings.stable || !matches.is_present(OPT_KEY) {
if !matches.is_present(OPT_KEY) {
// add a default selector matching the whole line
let key_settings = KeySettings::from(&settings);
settings.selectors.push(FieldSelector::new(
KeyPosition {
field: 1,
char: 1,
ignore_blanks: key_settings.ignore_blanks,
},
None,
key_settings,
));
settings.selectors.push(
FieldSelector::new(
KeyPosition {
field: 1,
char: 1,
ignore_blanks: key_settings.ignore_blanks,
},
None,
key_settings,
)
.unwrap(),
);
}
exec(&files, &settings)
@ -1108,7 +1238,7 @@ fn exec(files: &[String], settings: &GlobalSettings) -> i32 {
}
return check::check(files.first().unwrap(), settings);
} else {
let mut lines = files.iter().filter_map(open);
let mut lines = files.iter().map(open);
ext_sort(&mut lines, &settings);
}
@ -1203,6 +1333,8 @@ fn compare_by<'a>(a: &Line<'a>, b: &Line<'a>, global_settings: &GlobalSettings)
fn get_leading_gen(input: &str) -> Range<usize> {
let trimmed = input.trim_start();
let leading_whitespace_len = input.len() - trimmed.len();
// check for inf, -inf and nan
for allowed_prefix in &["inf", "-inf", "nan"] {
if trimmed.is_char_boundary(allowed_prefix.len())
&& trimmed[..allowed_prefix.len()].eq_ignore_ascii_case(allowed_prefix)
@ -1211,11 +1343,11 @@ fn get_leading_gen(input: &str) -> Range<usize> {
}
}
// Make this iter peekable to see if next char is numeric
let mut char_indices = trimmed.char_indices().peekable();
let mut char_indices = itertools::peek_nth(trimmed.char_indices());
let first = char_indices.peek();
if first.map_or(false, |&(_, c)| c == NEGATIVE || c == POSITIVE) {
if matches!(first, Some((_, NEGATIVE)) | Some((_, POSITIVE))) {
char_indices.next();
}
@ -1225,16 +1357,29 @@ fn get_leading_gen(input: &str) -> Range<usize> {
if c.is_ascii_digit() {
continue;
}
if c == DECIMAL_PT && !had_decimal_pt {
if c == DECIMAL_PT && !had_decimal_pt && !had_e_notation {
had_decimal_pt = true;
continue;
}
let next_char_numeric = char_indices
.peek()
.map_or(false, |(_, c)| c.is_ascii_digit());
if (c == 'e' || c == 'E') && !had_e_notation && next_char_numeric {
had_e_notation = true;
continue;
if (c == 'e' || c == 'E') && !had_e_notation {
// we can only consume the 'e' if what follow is either a digit, or a sign followed by a digit.
if let Some(&(_, next_char)) = char_indices.peek() {
if (next_char == '+' || next_char == '-')
&& matches!(
char_indices.peek_nth(2),
Some((_, c)) if c.is_ascii_digit()
)
{
// Consume the sign. The following digits will be consumed by the main loop.
char_indices.next();
had_e_notation = true;
continue;
}
if next_char.is_ascii_digit() {
had_e_notation = true;
continue;
}
}
}
return leading_whitespace_len..(leading_whitespace_len + idx);
}
@ -1390,18 +1535,17 @@ fn print_sorted<'a, T: Iterator<Item = &'a Line<'a>>>(iter: T, settings: &Global
}
// from cat.rs
fn open(path: impl AsRef<OsStr>) -> Option<Box<dyn Read + Send>> {
fn open(path: impl AsRef<OsStr>) -> Box<dyn Read + Send> {
let path = path.as_ref();
if path == "-" {
let stdin = stdin();
return Some(Box::new(stdin) as Box<dyn Read + Send>);
return Box::new(stdin) as Box<dyn Read + Send>;
}
match File::open(Path::new(path)) {
Ok(f) => Some(Box::new(f) as Box<dyn Read + Send>),
Ok(f) => Box::new(f) as Box<dyn Read + Send>,
Err(e) => {
show_error!("{0:?}: {1}", path, e.to_string());
None
crash!(2, "cannot read: {0:?}: {1}", path, e);
}
}
}
@ -1415,7 +1559,7 @@ mod tests {
fn test_get_hash() {
let a = "Ted".to_string();
assert_eq!(2646829031758483623, get_hash(&a));
assert_eq!(2_646_829_031_758_483_623, get_hash(&a));
}
#[test]

View file

@ -200,6 +200,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
split(&settings)
}
#[allow(dead_code)]
struct Settings {
prefix: String,
numeric_suffix: bool,
@ -210,7 +211,7 @@ struct Settings {
filter: Option<String>,
strategy: String,
strategy_param: String,
verbose: bool,
verbose: bool, // TODO: warning: field is never read: `verbose`
}
trait Splitter {

View file

@ -25,8 +25,7 @@ static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str =
"Run COMMAND, with modified buffering operations for its standard streams.\n\n\
Mandatory arguments to long options are mandatory for short options too.";
static LONG_HELP: &str =
"If MODE is 'L' the corresponding stream will be line buffered.\n\
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
This option is invalid with standard input.\n\n\
If MODE is '0' the corresponding stream will be unbuffered.\n\n\
Otherwise MODE is a number which may be followed by one of the following:\n\n\

View file

@ -199,6 +199,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
}
#[allow(clippy::if_same_then_else)]
if matches.is_present(options::FILE_SYSTEM) {
#[cfg(any(target_os = "linux", target_os = "windows"))]
syncfs(files);

View file

@ -121,13 +121,7 @@ impl Parser {
/// Test if the next token in the stream is a BOOLOP (-a or -o), without
/// removing the token from the stream.
fn peek_is_boolop(&mut self) -> bool {
// TODO: change to `matches!(self.peek(), Symbol::BoolOp(_))` once MSRV is 1.42
// #[allow(clippy::match_like_matches_macro)] // needs MSRV 1.43
if let Symbol::BoolOp(_) = self.peek() {
true
} else {
false
}
matches!(self.peek(), Symbol::BoolOp(_))
}
/// Parse an expression.
@ -271,11 +265,10 @@ impl Parser {
fn boolop(&mut self, op: Symbol) {
if op == Symbol::BoolOp(OsString::from("-a")) {
self.term();
self.stack.push(op);
} else {
self.expr();
self.stack.push(op);
}
self.stack.push(op);
}
/// Parse a (possible) unary argument test (string length or file

View file

@ -145,14 +145,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|| matches.is_present(options::sources::CURRENT)
{
let timestamp = if matches.is_present(options::sources::DATE) {
parse_date(matches.value_of(options::sources::DATE).unwrap().as_ref())
parse_date(matches.value_of(options::sources::DATE).unwrap())
} else {
parse_timestamp(
matches
.value_of(options::sources::CURRENT)
.unwrap()
.as_ref(),
)
parse_timestamp(matches.value_of(options::sources::CURRENT).unwrap())
};
(timestamp, timestamp)
} else {

View file

@ -110,7 +110,7 @@ impl<'a> Iterator for ExpandSet<'a> {
fn next(&mut self) -> Option<Self::Item> {
// while the Range has elements, try to return chars from it
// but make sure that they actually turn out to be Chars!
while let Some(n) = self.range.next() {
for n in &mut self.range {
if let Some(c) = from_u32(n) {
return Some(c);
}

View file

@ -15,16 +15,40 @@ use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind;
use std::path::Path;
#[derive(Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq)]
enum TruncateMode {
Absolute,
Reference,
Extend,
Reduce,
AtMost,
AtLeast,
RoundDown,
RoundUp,
Absolute(u64),
Extend(u64),
Reduce(u64),
AtMost(u64),
AtLeast(u64),
RoundDown(u64),
RoundUp(u64),
}
impl TruncateMode {
/// Compute a target size in bytes for this truncate mode.
///
/// `fsize` is the size of the reference file, in bytes.
///
/// # Examples
///
/// ```rust,ignore
/// let mode = TruncateMode::Extend(5);
/// let fsize = 10;
/// assert_eq!(mode.to_size(fsize), 15);
/// ```
fn to_size(&self, fsize: u64) -> u64 {
match self {
TruncateMode::Absolute(modsize) => *modsize,
TruncateMode::Extend(modsize) => fsize + modsize,
TruncateMode::Reduce(modsize) => fsize - modsize,
TruncateMode::AtMost(modsize) => fsize.min(*modsize),
TruncateMode::AtLeast(modsize) => fsize.max(*modsize),
TruncateMode::RoundDown(modsize) => fsize - fsize % modsize,
TruncateMode::RoundUp(modsize) => fsize + fsize % modsize,
}
}
}
static ABOUT: &str = "Shrink or extend the size of each file to the specified size.";
@ -116,116 +140,207 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let no_create = matches.is_present(options::NO_CREATE);
let reference = matches.value_of(options::REFERENCE).map(String::from);
let size = matches.value_of(options::SIZE).map(String::from);
if reference.is_none() && size.is_none() {
crash!(1, "you must specify either --reference or --size");
} else {
truncate(no_create, io_blocks, reference, size, files);
if let Err(e) = truncate(no_create, io_blocks, reference, size, files) {
match e.kind() {
ErrorKind::NotFound => {
// TODO Improve error-handling so that the error
// returned by `truncate()` provides the necessary
// parameter for formatting the error message.
let reference = matches.value_of(options::REFERENCE).map(String::from);
crash!(
1,
"cannot stat '{}': No such file or directory",
reference.unwrap()
);
}
_ => crash!(1, "{}", e.to_string()),
}
}
}
0
}
/// Truncate the named file to the specified size.
///
/// If `create` is true, then the file will be created if it does not
/// already exist. If `size` is larger than the number of bytes in the
/// file, then the file will be padded with zeros. If `size` is smaller
/// than the number of bytes in the file, then the file will be
/// truncated and any bytes beyond `size` will be lost.
///
/// # Errors
///
/// 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<()> {
let path = Path::new(filename);
let f = OpenOptions::new().write(true).create(create).open(path)?;
f.set_len(size)
}
/// Truncate files to a size relative to a given file.
///
/// `rfilename` is the name of the reference file.
///
/// `size_string` gives the size relative to the reference file to which
/// to set the target files. For example, "+3K" means "set each file to
/// be three kilobytes larger than the size of the reference file".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_and_size(
rfilename: &str,
size_string: &str,
filenames: Vec<String>,
create: bool,
) -> std::io::Result<()> {
let mode = match parse_mode_and_size(size_string) {
Ok(m) => match m {
TruncateMode::Absolute(_) => {
crash!(1, "you must specify a relative --size with --reference")
}
_ => m,
},
Err(_) => crash!(1, "Invalid number: {}", size_string),
};
let fsize = metadata(rfilename)?.len();
let tsize = mode.to_size(fsize);
for filename in &filenames {
file_truncate(filename, create, tsize)?;
}
Ok(())
}
/// Truncate files to match the size of a given reference file.
///
/// `rfilename` is the name of the reference file.
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_reference_file_only(
rfilename: &str,
filenames: Vec<String>,
create: bool,
) -> std::io::Result<()> {
let tsize = metadata(rfilename)?.len();
for filename in &filenames {
file_truncate(filename, create, tsize)?;
}
Ok(())
}
/// Truncate files to a specified size.
///
/// `size_string` gives either an absolute size or a relative size. A
/// relative size adjusts the size of each file relative to its current
/// size. For example, "3K" means "set each file to be three kilobytes"
/// whereas "+3K" means "set each file to be three kilobytes larger than
/// its current size".
///
/// If `create` is true, then each file will be created if it does not
/// already exist.
///
/// # Errors
///
/// If the any file could not be opened, or there was a problem setting
/// the size of at least one file.
fn truncate_size_only(
size_string: &str,
filenames: Vec<String>,
create: bool,
) -> std::io::Result<()> {
let mode = match parse_mode_and_size(size_string) {
Ok(m) => m,
Err(_) => crash!(1, "Invalid number: {}", size_string),
};
for filename in &filenames {
let fsize = metadata(filename).map(|m| m.len()).unwrap_or(0);
let tsize = mode.to_size(fsize);
file_truncate(filename, create, tsize)?;
}
Ok(())
}
fn truncate(
no_create: bool,
_: bool,
reference: Option<String>,
size: Option<String>,
filenames: Vec<String>,
) {
let (modsize, mode) = match size {
Some(size_string) => {
// Trim any whitespace.
let 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 mode = match c {
'+' => TruncateMode::Extend,
'-' => TruncateMode::Reduce,
'<' => TruncateMode::AtMost,
'>' => TruncateMode::AtLeast,
'/' => TruncateMode::RoundDown,
'%' => TruncateMode::RoundUp,
_ => TruncateMode::Absolute, /* assume that the size is just a number */
};
// If there was a modifier character, strip it.
let size_string = match mode {
TruncateMode::Absolute => size_string,
_ => &size_string[1..],
};
let num_bytes = match parse_size(size_string) {
Ok(b) => b,
Err(_) => crash!(1, "Invalid number: {}", size_string),
};
(num_bytes, mode)
}
None => (0, TruncateMode::Reference),
};
let refsize = match reference {
Some(ref rfilename) => {
match mode {
// Only Some modes work with a reference
TruncateMode::Reference => (), //No --size was given
TruncateMode::Extend => (),
TruncateMode::Reduce => (),
_ => crash!(1, "you must specify a relative --size with --reference"),
};
match metadata(rfilename) {
Ok(meta) => meta.len(),
Err(f) => match f.kind() {
ErrorKind::NotFound => {
crash!(1, "cannot stat '{}': No such file or directory", rfilename)
}
_ => crash!(1, "{}", f.to_string()),
},
}
}
None => 0,
};
for filename in &filenames {
let path = Path::new(filename);
match OpenOptions::new()
.read(true)
.write(true)
.create(!no_create)
.open(path)
{
Ok(file) => {
let fsize = match reference {
Some(_) => refsize,
None => match metadata(filename) {
Ok(meta) => meta.len(),
Err(f) => {
show_warning!("{}", f.to_string());
continue;
}
},
};
let tsize: u64 = match mode {
TruncateMode::Absolute => modsize,
TruncateMode::Reference => fsize,
TruncateMode::Extend => fsize + modsize,
TruncateMode::Reduce => fsize - modsize,
TruncateMode::AtMost => fsize.min(modsize),
TruncateMode::AtLeast => fsize.max(modsize),
TruncateMode::RoundDown => fsize - fsize % modsize,
TruncateMode::RoundUp => fsize + fsize % modsize,
};
match file.set_len(tsize) {
Ok(_) => {}
Err(f) => crash!(1, "{}", f.to_string()),
};
}
Err(f) => crash!(1, "{}", f.to_string()),
) -> std::io::Result<()> {
let create = !no_create;
// There are four possibilities
// - reference file given and size given,
// - reference file given but no size given,
// - no reference file given but size given,
// - no reference file given and no size given,
match (reference, size) {
(Some(rfilename), Some(size_string)) => {
truncate_reference_and_size(&rfilename, &size_string, filenames, create)
}
(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"),
}
}
/// Decide whether a character is one of the size modifiers, like '+' or '<'.
fn is_modifier(c: char) -> bool {
c == '+' || c == '-' || c == '<' || c == '>' || c == '/' || c == '%'
}
/// Parse a size string with optional modifier symbol as its first character.
///
/// A size string is as described in [`parse_size`]. The first character
/// of `size_string` might be a modifier symbol, like `'+'` or
/// `'<'`. The first element of the pair returned by this function
/// indicates which modifier symbol was present, or
/// [`TruncateMode::Absolute`] if none.
///
/// # Panics
///
/// If `size_string` is empty, or if no number could be parsed from the
/// given string (for example, if the string were `"abc"`).
///
/// # Examples
///
/// ```rust,ignore
/// assert_eq!(parse_mode_and_size("+123"), (TruncateMode::Extend, 123));
/// ```
fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ()> {
// Trim any whitespace.
let 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..]
} 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
@ -285,7 +400,9 @@ fn parse_size(size: &str) -> Result<u64, ()> {
#[cfg(test)]
mod tests {
use crate::parse_mode_and_size;
use crate::parse_size;
use crate::TruncateMode;
#[test]
fn test_parse_size_zero() {
@ -311,4 +428,15 @@ mod tests {
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)));
assert_eq!(parse_mode_and_size("+10"), Ok(TruncateMode::Extend(10)));
assert_eq!(parse_mode_and_size("-10"), Ok(TruncateMode::Reduce(10)));
assert_eq!(parse_mode_and_size("<10"), Ok(TruncateMode::AtMost(10)));
assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
}
}

View file

@ -6,19 +6,14 @@
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
/* last synced with: whoami (GNU coreutils) 8.22 */
// Allow dead code here in order to keep all fields, constants here, for consistency.
#![allow(dead_code)]
#[macro_use]
extern crate uucore;
use uucore::utmpx::*;
use clap::{App, Arg};
use uucore::utmpx::{self, Utmpx};
static ABOUT: &str = "Display who is currently logged in, according to FILE.";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "Print the user names of users currently logged in to the current host";
static ARG_FILES: &str = "files";
@ -26,13 +21,23 @@ fn get_usage() -> String {
format!("{0} [FILE]", executable!())
}
fn get_long_usage() -> String {
format!(
"Output who is currently logged in according to FILE.
If FILE is not specified, use {}. /var/log/wtmp as FILE is common.",
utmpx::DEFAULT_FILE
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = get_usage();
let after_help = get_long_usage();
let matches = App::new(executable!())
.version(VERSION)
.about(ABOUT)
.usage(&usage[..])
.after_help(&after_help[..])
.arg(Arg::with_name(ARG_FILES).takes_value(true).max_values(1))
.get_matches_from(args);
@ -44,7 +49,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let filename = if !files.is_empty() {
files[0].as_ref()
} else {
DEFAULT_FILE
utmpx::DEFAULT_FILE
};
let mut users = Utmpx::iter_all_records()

View file

@ -551,10 +551,11 @@ impl Who {
" ?".into()
};
let mut s = ut.host();
if self.do_lookup {
s = safe_unwrap!(ut.canon_host());
}
let s = if self.do_lookup {
safe_unwrap!(ut.canon_host())
} else {
ut.host()
};
let hoststr = if s.is_empty() { s } else { format!("({})", s) };
self.print_line(

View file

@ -179,7 +179,7 @@ impl MountInfo {
/* for Irix 6.5 */
| "ignore" => self.dummy = true,
_ => self.dummy = self.fs_type == "none"
&& self.mount_option.find(MOUNT_OPT_BIND).is_none(),
&& !self.mount_option.contains(MOUNT_OPT_BIND)
}
// set MountInfo::remote
#[cfg(windows)]

View file

@ -40,6 +40,7 @@ pub enum ExitStatus {
Signal(i32),
}
#[allow(clippy::trivially_copy_pass_by_ref)]
impl ExitStatus {
fn from_std_status(status: StdExitStatus) -> Self {
#[cfg(unix)]

View file

@ -146,15 +146,16 @@ pub trait Args: Iterator<Item = OsString> + Sized {
InvalidEncodingHandling::Ignore => s.is_ok(),
_ => true,
})
.map(|s| match s.is_ok() {
true => s.unwrap(),
false => s.unwrap_err(),
.map(|s| match s {
Ok(v) => v,
Err(e) => e,
})
.collect();
match full_conversion {
true => ConversionResult::Complete(result_vector),
false => ConversionResult::Lossy(result_vector),
if full_conversion {
ConversionResult::Complete(result_vector)
} else {
ConversionResult::Lossy(result_vector)
}
}

View file

@ -33,7 +33,7 @@ pub fn determine_backup_suffix(supplied_suffix: Option<&str>) -> String {
if let Some(suffix) = supplied_suffix {
String::from(suffix)
} else {
env::var("SIMPLE_BACKUP_SUFFIX").unwrap_or("~".to_owned())
env::var("SIMPLE_BACKUP_SUFFIX").unwrap_or_else(|_| "~".to_owned())
}
}

View file

@ -1,6 +1,3 @@
#![allow(dead_code)] // work-around for GH:rust-lang/rust#62127; maint: can be removed when MinSRV >= v1.38.0
#![allow(unused_macros)] // work-around for GH:rust-lang/rust#62127; maint: can be removed when MinSRV >= v1.38.0
// Copyright (C) ~ Roy Ivy III <rivy.dev@gmail.com>; MIT license
extern crate proc_macro;
@ -44,7 +41,6 @@ impl syn::parse::Parse for Tokens {
}
#[proc_macro]
#[cfg(not(test))] // work-around for GH:rust-lang/rust#62127; maint: can be removed when MinSRV >= v1.38.0
pub fn main(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
let Tokens { expr } = syn::parse_macro_input!(stream as Tokens);
proc_dbg!(&expr);

View file

@ -34,7 +34,7 @@ fn test_base32_encode_file() {
#[test]
fn test_decode() {
for decode_param in vec!["-d", "--decode"] {
for decode_param in &["-d", "--decode"] {
let input = "JBSWY3DPFQQFO33SNRSCC===\n";
new_ucmd!()
.arg(decode_param)
@ -56,7 +56,7 @@ fn test_garbage() {
#[test]
fn test_ignore_garbage() {
for ignore_garbage_param in vec!["-i", "--ignore-garbage"] {
for ignore_garbage_param in &["-i", "--ignore-garbage"] {
let input = "JBSWY\x013DPFQ\x02QFO33SNRSCC===\n";
new_ucmd!()
.arg("-d")
@ -69,7 +69,7 @@ fn test_ignore_garbage() {
#[test]
fn test_wrap() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
let input = "The quick brown fox jumps over the lazy dog.";
new_ucmd!()
.arg(wrap_param)
@ -84,16 +84,21 @@ fn test_wrap() {
#[test]
fn test_wrap_no_arg() {
for wrap_param in vec!["-w", "--wrap"] {
new_ucmd!().arg(wrap_param).fails().stderr_only(format!(
"error: The argument '--wrap <wrap>\' requires a value but none was supplied\n\nUSAGE:\n base32 [OPTION]... [FILE]\n\nFor more information try --help"
));
for wrap_param in &["-w", "--wrap"] {
let expected_stderr = "error: The argument '--wrap <wrap>\' requires a value but none was \
supplied\n\nUSAGE:\n base32 [OPTION]... [FILE]\n\nFor more \
information try --help"
.to_string();
new_ucmd!()
.arg(wrap_param)
.fails()
.stderr_only(expected_stderr);
}
}
#[test]
fn test_wrap_bad_arg() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
new_ucmd!()
.arg(wrap_param)
.arg("b")

View file

@ -26,7 +26,7 @@ fn test_base64_encode_file() {
#[test]
fn test_decode() {
for decode_param in vec!["-d", "--decode"] {
for decode_param in &["-d", "--decode"] {
let input = "aGVsbG8sIHdvcmxkIQ==";
new_ucmd!()
.arg(decode_param)
@ -48,7 +48,7 @@ fn test_garbage() {
#[test]
fn test_ignore_garbage() {
for ignore_garbage_param in vec!["-i", "--ignore-garbage"] {
for ignore_garbage_param in &["-i", "--ignore-garbage"] {
let input = "aGVsbG8sIHdvcmxkIQ==\0";
new_ucmd!()
.arg("-d")
@ -61,7 +61,7 @@ fn test_ignore_garbage() {
#[test]
fn test_wrap() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
let input = "The quick brown fox jumps over the lazy dog.";
new_ucmd!()
.arg(wrap_param)
@ -74,7 +74,7 @@ fn test_wrap() {
#[test]
fn test_wrap_no_arg() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
new_ucmd!().arg(wrap_param).fails().stderr_contains(
&"The argument '--wrap <wrap>' requires a value but none was supplied",
);
@ -83,7 +83,7 @@ fn test_wrap_no_arg() {
#[test]
fn test_wrap_bad_arg() {
for wrap_param in vec!["-w", "--wrap"] {
for wrap_param in &["-w", "--wrap"] {
new_ucmd!()
.arg(wrap_param)
.arg("b")

View file

@ -4,7 +4,7 @@ use std::ffi::OsStr;
#[test]
fn test_help() {
for help_flg in vec!["-h", "--help"] {
for help_flg in &["-h", "--help"] {
new_ucmd!()
.arg(&help_flg)
.succeeds()
@ -15,7 +15,7 @@ fn test_help() {
#[test]
fn test_version() {
for version_flg in vec!["-V", "--version"] {
for version_flg in &["-V", "--version"] {
assert!(new_ucmd!()
.arg(&version_flg)
.succeeds()
@ -59,7 +59,7 @@ fn test_dont_remove_suffix() {
#[test]
fn test_multiple_param() {
for multiple_param in vec!["-a", "--multiple"] {
for &multiple_param in &["-a", "--multiple"] {
let path = "/foo/bar/baz";
new_ucmd!()
.args(&[multiple_param, path, path])
@ -70,7 +70,7 @@ fn test_multiple_param() {
#[test]
fn test_suffix_param() {
for suffix_param in vec!["-s", "--suffix"] {
for &suffix_param in &["-s", "--suffix"] {
let path = "/foo/bar/baz.exe";
new_ucmd!()
.args(&[suffix_param, ".exe", path, path])
@ -81,7 +81,7 @@ fn test_suffix_param() {
#[test]
fn test_zero_param() {
for zero_param in vec!["-z", "--zero"] {
for &zero_param in &["-z", "--zero"] {
let path = "/foo/bar/baz";
new_ucmd!()
.args(&[zero_param, "-a", path, path])
@ -91,7 +91,12 @@ fn test_zero_param() {
}
fn expect_error(input: Vec<&str>) {
assert!(new_ucmd!().args(&input).fails().no_stdout().stderr().len() > 0);
assert!(!new_ucmd!()
.args(&input)
.fails()
.no_stdout()
.stderr_str()
.is_empty());
}
#[test]

View file

@ -237,7 +237,7 @@ fn test_numbered_lines_no_trailing_newline() {
#[test]
fn test_stdin_show_nonprinting() {
for same_param in vec!["-v", "--show-nonprinting"] {
for same_param in &["-v", "--show-nonprinting"] {
new_ucmd!()
.args(&[same_param])
.pipe_in("\t\0\n")
@ -248,7 +248,7 @@ fn test_stdin_show_nonprinting() {
#[test]
fn test_stdin_show_tabs() {
for same_param in vec!["-T", "--show-tabs"] {
for same_param in &["-T", "--show-tabs"] {
new_ucmd!()
.args(&[same_param])
.pipe_in("\t\0\n")
@ -259,7 +259,7 @@ fn test_stdin_show_tabs() {
#[test]
fn test_stdin_show_ends() {
for same_param in vec!["-E", "--show-ends"] {
for &same_param in &["-E", "--show-ends"] {
new_ucmd!()
.args(&[same_param, "-"])
.pipe_in("\t\0\n\t")
@ -270,7 +270,7 @@ fn test_stdin_show_ends() {
#[test]
fn test_stdin_show_all() {
for same_param in vec!["-A", "--show-all"] {
for same_param in &["-A", "--show-all"] {
new_ucmd!()
.args(&[same_param])
.pipe_in("\t\0\n")
@ -299,7 +299,7 @@ fn test_stdin_nonprinting_and_tabs() {
#[test]
fn test_stdin_squeeze_blank() {
for same_param in vec!["-s", "--squeeze-blank"] {
for same_param in &["-s", "--squeeze-blank"] {
new_ucmd!()
.arg(same_param)
.pipe_in("\n\na\n\n\n\n\nb\n\n\n")
@ -310,7 +310,7 @@ fn test_stdin_squeeze_blank() {
#[test]
fn test_stdin_number_non_blank() {
for same_param in vec!["-b", "--number-nonblank"] {
for same_param in &["-b", "--number-nonblank"] {
new_ucmd!()
.arg(same_param)
.arg("-")
@ -322,7 +322,7 @@ fn test_stdin_number_non_blank() {
#[test]
fn test_non_blank_overrides_number() {
for same_param in vec!["-b", "--number-nonblank"] {
for &same_param in &["-b", "--number-nonblank"] {
new_ucmd!()
.args(&[same_param, "-"])
.pipe_in("\na\nb\n\n\nc")
@ -333,7 +333,7 @@ fn test_non_blank_overrides_number() {
#[test]
fn test_squeeze_blank_before_numbering() {
for same_param in vec!["-s", "--squeeze-blank"] {
for &same_param in &["-s", "--squeeze-blank"] {
new_ucmd!()
.args(&[same_param, "-n", "-"])
.pipe_in("a\n\n\nb")
@ -408,7 +408,10 @@ fn test_domain_socket() {
use std::thread;
use unix_socket::UnixListener;
let dir = tempfile::Builder::new().prefix("unix_socket").tempdir().expect("failed to create dir");
let dir = tempfile::Builder::new()
.prefix("unix_socket")
.tempdir()
.expect("failed to create dir");
let socket_path = dir.path().join("sock");
let listener = UnixListener::bind(&socket_path).expect("failed to create socket");
@ -426,7 +429,7 @@ fn test_domain_socket() {
let child = new_ucmd!().args(&[socket_path]).run_no_wait();
barrier.wait();
let stdout = &child.wait_with_output().unwrap().stdout.clone();
let stdout = &child.wait_with_output().unwrap().stdout;
let output = String::from_utf8_lossy(&stdout);
assert_eq!("a\tb", output);

View file

@ -6,7 +6,7 @@ fn test_invalid_option() {
new_ucmd!().arg("-w").arg("/").fails();
}
static DIR: &'static str = "/tmp";
static DIR: &str = "/tmp";
#[test]
fn test_invalid_group() {

View file

@ -8,8 +8,8 @@ use self::chmod::strip_minus_from_mode;
extern crate chmod;
use self::libc::umask;
static TEST_FILE: &'static str = "file";
static REFERENCE_FILE: &'static str = "reference";
static TEST_FILE: &str = "file";
static REFERENCE_FILE: &str = "reference";
static REFERENCE_PERMS: u32 = 0o247;
lazy_static! {
static ref UMASK_MUTEX: Mutex<()> = Mutex::new(());
@ -69,6 +69,7 @@ fn run_tests(tests: Vec<TestCase>) {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_octal() {
let tests = vec![
TestCase {
@ -121,6 +122,7 @@ fn test_chmod_octal() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_ugoa() {
let _guard = UMASK_MUTEX.lock();
@ -216,6 +218,7 @@ fn test_chmod_ugoa() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_ugo_copy() {
let tests = vec![
TestCase {
@ -248,6 +251,7 @@ fn test_chmod_ugo_copy() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_many_options() {
let _guard = UMASK_MUTEX.lock();
@ -264,6 +268,7 @@ fn test_chmod_many_options() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_reference_file() {
let tests = vec![
TestCase {
@ -303,6 +308,7 @@ fn test_permission_denied() {
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_chmod_recursive() {
let _guard = UMASK_MUTEX.lock();
@ -477,7 +483,7 @@ fn test_chmod_strip_minus_from_mode() {
];
for test in tests {
let mut args: Vec<String> = test.0.split(" ").map(|v| v.to_string()).collect();
let mut args: Vec<String> = test.0.split(' ').map(|v| v.to_string()).collect();
let _mode_had_minus_prefix = strip_minus_from_mode(&mut args);
assert_eq!(test.1, args.join(" "));
}

View file

@ -39,7 +39,7 @@ mod test_passgrp {
#[test]
fn test_usr2uid() {
assert_eq!(0, usr2uid("root").unwrap());
assert!(usr2uid("88888888").is_err());
assert!(usr2uid("88_888_888").is_err());
assert!(usr2uid("auserthatdoesntexist").is_err());
}
@ -50,14 +50,14 @@ mod test_passgrp {
} else {
assert_eq!(0, grp2gid("wheel").unwrap());
}
assert!(grp2gid("88888888").is_err());
assert!(grp2gid("88_888_888").is_err());
assert!(grp2gid("agroupthatdoesntexist").is_err());
}
#[test]
fn test_uid2usr() {
assert_eq!("root", uid2usr(0).unwrap());
assert!(uid2usr(88888888).is_err());
assert!(uid2usr(88_888_888).is_err());
}
#[test]
@ -67,7 +67,7 @@ mod test_passgrp {
} else {
assert_eq!("wheel", gid2grp(0).unwrap());
}
assert!(gid2grp(88888888).is_err());
assert!(gid2grp(88_888_888).is_err());
}
}

View file

@ -85,12 +85,12 @@ fn test_crc_for_bigger_than_32_bytes() {
let result = ucmd.arg("chars.txt").succeeds();
let mut stdout_splitted = result.stdout_str().split(" ");
let mut stdout_splitted = result.stdout_str().split(' ');
let cksum: i64 = stdout_splitted.next().unwrap().parse().unwrap();
let bytes_cnt: i64 = stdout_splitted.next().unwrap().parse().unwrap();
assert_eq!(cksum, 586047089);
assert_eq!(cksum, 586_047_089);
assert_eq!(bytes_cnt, 16);
}
@ -100,11 +100,11 @@ fn test_stdin_larger_than_128_bytes() {
let result = ucmd.arg("larger_than_2056_bytes.txt").succeeds();
let mut stdout_splitted = result.stdout_str().split(" ");
let mut stdout_splitted = result.stdout_str().split(' ');
let cksum: i64 = stdout_splitted.next().unwrap().parse().unwrap();
let bytes_cnt: i64 = stdout_splitted.next().unwrap().parse().unwrap();
assert_eq!(cksum, 945881979);
assert_eq!(cksum, 945_881_979);
assert_eq!(bytes_cnt, 2058);
}

View file

@ -74,7 +74,7 @@ fn output_delimiter_require_arg() {
#[cfg_attr(not(feature = "test_unimplemented"), ignore)]
#[test]
fn zero_terminated() {
for param in vec!["-z", "--zero-terminated"] {
for &param in &["-z", "--zero-terminated"] {
new_ucmd!()
.args(&[param, "a", "b"])
.fails()

View file

@ -108,7 +108,7 @@ fn test_cp_multiple_files() {
#[test]
// FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590
#[cfg(not(macos))]
#[cfg(not(target_os = "macos"))]
fn test_cp_recurse() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("-r")
@ -132,7 +132,7 @@ fn test_cp_with_dirs_t() {
#[test]
// FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590
#[cfg(not(macos))]
#[cfg(not(target_os = "macos"))]
fn test_cp_with_dirs() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

View file

@ -1,13 +1,13 @@
use crate::common::util::*;
static INPUT: &'static str = "lists.txt";
static INPUT: &str = "lists.txt";
struct TestedSequence<'b> {
name: &'b str,
sequence: &'b str,
}
static EXAMPLE_SEQUENCES: &'static [TestedSequence<'static>] = &[
static EXAMPLE_SEQUENCES: &[TestedSequence] = &[
TestedSequence {
name: "singular",
sequence: "2",
@ -34,14 +34,14 @@ static EXAMPLE_SEQUENCES: &'static [TestedSequence<'static>] = &[
},
];
static COMPLEX_SEQUENCE: &'static TestedSequence<'static> = &TestedSequence {
static COMPLEX_SEQUENCE: &TestedSequence = &TestedSequence {
name: "",
sequence: "9-,6-7,-2,4",
};
#[test]
fn test_byte_sequence() {
for param in vec!["-b", "--bytes"] {
for &param in &["-b", "--bytes"] {
for example_seq in EXAMPLE_SEQUENCES {
new_ucmd!()
.args(&[param, example_seq.sequence, INPUT])
@ -53,7 +53,7 @@ fn test_byte_sequence() {
#[test]
fn test_char_sequence() {
for param in vec!["-c", "--characters"] {
for &param in &["-c", "--characters"] {
for example_seq in EXAMPLE_SEQUENCES {
//as of coreutils 8.25 a char range is effectively the same as a byte range; there is no distinct treatment of utf8 chars.
new_ucmd!()
@ -66,7 +66,7 @@ fn test_char_sequence() {
#[test]
fn test_field_sequence() {
for param in vec!["-f", "--fields"] {
for &param in &["-f", "--fields"] {
for example_seq in EXAMPLE_SEQUENCES {
new_ucmd!()
.args(&[param, example_seq.sequence, INPUT])
@ -78,7 +78,7 @@ fn test_field_sequence() {
#[test]
fn test_specify_delimiter() {
for param in vec!["-d", "--delimiter"] {
for &param in &["-d", "--delimiter"] {
new_ucmd!()
.args(&[param, ":", "-f", COMPLEX_SEQUENCE.sequence, INPUT])
.succeeds()
@ -122,7 +122,7 @@ fn test_zero_terminated() {
#[test]
fn test_only_delimited() {
for param in vec!["-s", "--only-delimited"] {
for param in &["-s", "--only-delimited"] {
new_ucmd!()
.args(&["-d_", param, "-f", "1"])
.pipe_in("91\n82\n7_3")

View file

@ -187,7 +187,7 @@ fn test_du_d_flag() {
// TODO: gnu `du` doesn't use trailing "/" here
// result.stdout_str(), result_reference.stdout_str()
result.stdout_str().trim_end_matches("/\n"),
result_reference.stdout_str().trim_end_matches("\n")
result_reference.stdout_str().trim_end_matches('\n')
);
return;
}

View file

@ -118,7 +118,7 @@ fn test_null_delimiter() {
let mut vars: Vec<_> = out.split('\0').collect();
assert_eq!(vars.len(), 3);
vars.sort();
vars.sort_unstable();
assert_eq!(vars[0], "");
assert_eq!(vars[1], "ABC=xyz");
assert_eq!(vars[2], "FOO=bar");
@ -135,7 +135,7 @@ fn test_unset_variable() {
.succeeds()
.stdout_move_str();
assert_eq!(out.lines().any(|line| line.starts_with("HOME=")), false);
assert!(!out.lines().any(|line| line.starts_with("HOME=")));
}
#[test]
@ -196,7 +196,7 @@ fn test_change_directory() {
fn test_fail_change_directory() {
let scene = TestScenario::new(util_name!());
let some_non_existing_path = "some_nonexistent_path";
assert_eq!(Path::new(some_non_existing_path).is_dir(), false);
assert!(!Path::new(some_non_existing_path).is_dir());
let out = scene
.ucmd()

View file

@ -2,55 +2,95 @@ use crate::common::util::*;
#[test]
fn test_simple_arithmetic() {
new_ucmd!().args(&["1", "+", "1"]).run().stdout_is("2\n");
new_ucmd!()
.args(&["1", "+", "1"])
.succeeds()
.stdout_only("2\n");
new_ucmd!().args(&["1", "-", "1"]).run().stdout_is("0\n");
new_ucmd!()
.args(&["1", "-", "1"])
.fails()
.status_code(1)
.stdout_only("0\n");
new_ucmd!().args(&["3", "*", "2"]).run().stdout_is("6\n");
new_ucmd!()
.args(&["3", "*", "2"])
.succeeds()
.stdout_only("6\n");
new_ucmd!().args(&["4", "/", "2"]).run().stdout_is("2\n");
new_ucmd!()
.args(&["4", "/", "2"])
.succeeds()
.stdout_only("2\n");
}
#[test]
fn test_complex_arithmetic() {
let run = new_ucmd!()
new_ucmd!()
.args(&["9223372036854775807", "+", "9223372036854775807"])
.run();
run.stdout_is("");
run.stderr_is("expr: +: Numerical result out of range");
.succeeds()
.stdout_only("18446744073709551614\n");
let run = new_ucmd!().args(&["9", "/", "0"]).run();
run.stdout_is("");
run.stderr_is("expr: division by zero");
new_ucmd!()
.args(&[
"92233720368547758076549841651981984981498415651",
"%",
"922337203685",
])
.succeeds()
.stdout_only("533691697086\n");
new_ucmd!()
.args(&[
"92233720368547758076549841651981984981498415651",
"*",
"922337203685",
])
.succeeds()
.stdout_only("85070591730190566808700855121818604965830915152801178873935\n");
new_ucmd!()
.args(&[
"92233720368547758076549841651981984981498415651",
"-",
"922337203685",
])
.succeeds()
.stdout_only("92233720368547758076549841651981984059161211966\n");
new_ucmd!()
.args(&["9", "/", "0"])
.fails()
.stderr_only("expr: division by zero\n");
}
#[test]
fn test_parenthesis() {
new_ucmd!()
.args(&["(", "1", "+", "1", ")", "*", "2"])
.run()
.stdout_is("4\n");
.succeeds()
.stdout_only("4\n");
}
#[test]
fn test_or() {
new_ucmd!()
.args(&["0", "|", "foo"])
.run()
.stdout_is("foo\n");
.succeeds()
.stdout_only("foo\n");
new_ucmd!()
.args(&["foo", "|", "bar"])
.run()
.stdout_is("foo\n");
.succeeds()
.stdout_only("foo\n");
}
#[test]
fn test_and() {
new_ucmd!()
.args(&["foo", "&", "1"])
.run()
.stdout_is("foo\n");
.succeeds()
.stdout_only("foo\n");
new_ucmd!().args(&["", "&", "1"]).run().stdout_is("0\n");
}

View file

@ -4,6 +4,7 @@
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
#![allow(clippy::unreadable_literal)]
use crate::common::util::*;
use std::time::SystemTime;
@ -39,6 +40,18 @@ fn test_first_100000_integers() {
assert_eq!(hash_check, "4ed2d8403934fa1c76fe4b84c5d4b8850299c359");
}
#[test]
fn test_cli_args() {
// Make sure that factor works with CLI arguments as well.
new_ucmd!().args(&["3"]).succeeds().stdout_contains("3: 3");
new_ucmd!()
.args(&["3", "6"])
.succeeds()
.stdout_contains("3: 3")
.stdout_contains("6: 2 3");
}
#[test]
fn test_random() {
use conv::prelude::*;
@ -77,7 +90,7 @@ fn test_random() {
};
}
factors.sort();
factors.sort_unstable();
(product, factors)
};
@ -92,7 +105,7 @@ fn test_random() {
for factor in factors {
outstring.push_str(&(format!(" {}", factor))[..]);
}
outstring.push_str("\n");
outstring.push('\n');
}
run(instring.as_bytes(), outstring.as_bytes());
@ -131,7 +144,7 @@ fn test_random_big() {
f_bits.push(extrarange.sample(&mut rng));
}
f_bits.push(extrabits);
f_bits.sort();
f_bits.sort_unstable();
// compute sequential differences here. We leave off the +14 bits
// so we can just index PRIMES_BY_BITS
@ -160,7 +173,7 @@ fn test_random_big() {
}
assert_eq!(nbits, 64);
factors.sort();
factors.sort_unstable();
(product, factors)
};
@ -174,7 +187,7 @@ fn test_random_big() {
for factor in factors {
outstring.push_str(&(format!(" {}", factor))[..]);
}
outstring.push_str("\n");
outstring.push('\n');
}
run(instring.as_bytes(), outstring.as_bytes());
@ -202,7 +215,7 @@ fn run(instring: &[u8], outstring: &[u8]) {
.stdout_is(String::from_utf8(outstring.to_owned()).unwrap());
}
const PRIMES_BY_BITS: &'static [&'static [u64]] = &[
const PRIMES_BY_BITS: &[&[u64]] = &[
PRIMES14, PRIMES15, PRIMES16, PRIMES17, PRIMES18, PRIMES19, PRIMES20, PRIMES21, PRIMES22,
PRIMES23, PRIMES24, PRIMES25, PRIMES26, PRIMES27, PRIMES28, PRIMES29, PRIMES30, PRIMES31,
PRIMES32, PRIMES33, PRIMES34, PRIMES35, PRIMES36, PRIMES37, PRIMES38, PRIMES39, PRIMES40,
@ -210,7 +223,7 @@ const PRIMES_BY_BITS: &'static [&'static [u64]] = &[
PRIMES50,
];
const PRIMES64: &'static [u64] = &[
const PRIMES64: &[u64] = &[
18446744073709551557,
18446744073709551533,
18446744073709551521,
@ -262,7 +275,7 @@ const PRIMES64: &'static [u64] = &[
18446744073709549571,
];
const PRIMES14: &'static [u64] = &[
const PRIMES14: &[u64] = &[
16381, 16369, 16363, 16361, 16349, 16339, 16333, 16319, 16301, 16273, 16267, 16253, 16249,
16231, 16229, 16223, 16217, 16193, 16189, 16187, 16183, 16141, 16139, 16127, 16111, 16103,
16097, 16091, 16087, 16073, 16069, 16067, 16063, 16061, 16057, 16033, 16007, 16001, 15991,
@ -274,7 +287,7 @@ const PRIMES14: &'static [u64] = &[
15373,
];
const PRIMES15: &'static [u64] = &[
const PRIMES15: &[u64] = &[
32749, 32719, 32717, 32713, 32707, 32693, 32687, 32653, 32647, 32633, 32621, 32611, 32609,
32603, 32587, 32579, 32573, 32569, 32563, 32561, 32537, 32533, 32531, 32507, 32503, 32497,
32491, 32479, 32467, 32443, 32441, 32429, 32423, 32413, 32411, 32401, 32381, 32377, 32371,
@ -285,7 +298,7 @@ const PRIMES15: &'static [u64] = &[
31847, 31817, 31799, 31793, 31771, 31769, 31751,
];
const PRIMES16: &'static [u64] = &[
const PRIMES16: &[u64] = &[
65521, 65519, 65497, 65479, 65449, 65447, 65437, 65423, 65419, 65413, 65407, 65393, 65381,
65371, 65357, 65353, 65327, 65323, 65309, 65293, 65287, 65269, 65267, 65257, 65239, 65213,
65203, 65183, 65179, 65173, 65171, 65167, 65147, 65141, 65129, 65123, 65119, 65111, 65101,
@ -295,7 +308,7 @@ const PRIMES16: &'static [u64] = &[
64627, 64621, 64613, 64609, 64601, 64591, 64579, 64577, 64567, 64553,
];
const PRIMES17: &'static [u64] = &[
const PRIMES17: &[u64] = &[
131071, 131063, 131059, 131041, 131023, 131011, 131009, 130987, 130981, 130973, 130969, 130957,
130927, 130873, 130859, 130843, 130841, 130829, 130817, 130811, 130807, 130787, 130783, 130769,
130729, 130699, 130693, 130687, 130681, 130657, 130651, 130649, 130643, 130639, 130633, 130631,
@ -306,7 +319,7 @@ const PRIMES17: &'static [u64] = &[
130073, 130069, 130057, 130051,
];
const PRIMES18: &'static [u64] = &[
const PRIMES18: &[u64] = &[
262139, 262133, 262127, 262121, 262111, 262109, 262103, 262079, 262069, 262051, 262049, 262027,
262007, 261983, 261977, 261973, 261971, 261959, 261917, 261887, 261881, 261847, 261823, 261799,
261791, 261787, 261773, 261761, 261757, 261739, 261721, 261713, 261707, 261697, 261673, 261643,
@ -316,7 +329,7 @@ const PRIMES18: &'static [u64] = &[
261169, 261167, 261127,
];
const PRIMES19: &'static [u64] = &[
const PRIMES19: &[u64] = &[
524287, 524269, 524261, 524257, 524243, 524231, 524221, 524219, 524203, 524201, 524197, 524189,
524171, 524149, 524123, 524119, 524113, 524099, 524087, 524081, 524071, 524063, 524057, 524053,
524047, 523997, 523987, 523969, 523949, 523937, 523927, 523907, 523903, 523877, 523867, 523847,
@ -326,7 +339,7 @@ const PRIMES19: &'static [u64] = &[
523403, 523387, 523357, 523351, 523349, 523333, 523307, 523297,
];
const PRIMES20: &'static [u64] = &[
const PRIMES20: &[u64] = &[
1048573, 1048571, 1048559, 1048549, 1048517, 1048507, 1048447, 1048433, 1048423, 1048391,
1048387, 1048367, 1048361, 1048357, 1048343, 1048309, 1048291, 1048273, 1048261, 1048219,
1048217, 1048213, 1048193, 1048189, 1048139, 1048129, 1048127, 1048123, 1048063, 1048051,
@ -336,7 +349,7 @@ const PRIMES20: &'static [u64] = &[
1047691, 1047689, 1047671, 1047667, 1047653, 1047649, 1047647, 1047589, 1047587, 1047559,
];
const PRIMES21: &'static [u64] = &[
const PRIMES21: &[u64] = &[
2097143, 2097133, 2097131, 2097097, 2097091, 2097083, 2097047, 2097041, 2097031, 2097023,
2097013, 2096993, 2096987, 2096971, 2096959, 2096957, 2096947, 2096923, 2096911, 2096909,
2096893, 2096881, 2096873, 2096867, 2096851, 2096837, 2096807, 2096791, 2096789, 2096777,
@ -346,7 +359,7 @@ const PRIMES21: &'static [u64] = &[
2096221, 2096209, 2096191, 2096183, 2096147,
];
const PRIMES22: &'static [u64] = &[
const PRIMES22: &[u64] = &[
4194301, 4194287, 4194277, 4194271, 4194247, 4194217, 4194199, 4194191, 4194187, 4194181,
4194173, 4194167, 4194143, 4194137, 4194131, 4194107, 4194103, 4194023, 4194011, 4194007,
4193977, 4193971, 4193963, 4193957, 4193939, 4193929, 4193909, 4193869, 4193807, 4193803,
@ -356,7 +369,7 @@ const PRIMES22: &'static [u64] = &[
4193297,
];
const PRIMES23: &'static [u64] = &[
const PRIMES23: &[u64] = &[
8388593, 8388587, 8388581, 8388571, 8388547, 8388539, 8388473, 8388461, 8388451, 8388449,
8388439, 8388427, 8388421, 8388409, 8388377, 8388371, 8388319, 8388301, 8388287, 8388283,
8388277, 8388239, 8388209, 8388187, 8388113, 8388109, 8388091, 8388071, 8388059, 8388019,
@ -365,7 +378,7 @@ const PRIMES23: &'static [u64] = &[
8387723, 8387707, 8387671, 8387611, 8387609, 8387591,
];
const PRIMES24: &'static [u64] = &[
const PRIMES24: &[u64] = &[
16777213, 16777199, 16777183, 16777153, 16777141, 16777139, 16777127, 16777121, 16777099,
16777049, 16777027, 16776989, 16776973, 16776971, 16776967, 16776961, 16776941, 16776937,
16776931, 16776919, 16776901, 16776899, 16776869, 16776857, 16776839, 16776833, 16776817,
@ -375,7 +388,7 @@ const PRIMES24: &'static [u64] = &[
16776317, 16776313, 16776289, 16776217, 16776211,
];
const PRIMES25: &'static [u64] = &[
const PRIMES25: &[u64] = &[
33554393, 33554383, 33554371, 33554347, 33554341, 33554317, 33554291, 33554273, 33554267,
33554249, 33554239, 33554221, 33554201, 33554167, 33554159, 33554137, 33554123, 33554093,
33554083, 33554077, 33554051, 33554021, 33554011, 33554009, 33553999, 33553991, 33553969,
@ -385,7 +398,7 @@ const PRIMES25: &'static [u64] = &[
33553519, 33553517, 33553511, 33553489, 33553463, 33553451, 33553417,
];
const PRIMES26: &'static [u64] = &[
const PRIMES26: &[u64] = &[
67108859, 67108837, 67108819, 67108777, 67108763, 67108757, 67108753, 67108747, 67108739,
67108729, 67108721, 67108709, 67108693, 67108669, 67108667, 67108661, 67108649, 67108633,
67108597, 67108579, 67108529, 67108511, 67108507, 67108493, 67108471, 67108463, 67108453,
@ -396,7 +409,7 @@ const PRIMES26: &'static [u64] = &[
67107863,
];
const PRIMES27: &'static [u64] = &[
const PRIMES27: &[u64] = &[
134217689, 134217649, 134217617, 134217613, 134217593, 134217541, 134217529, 134217509,
134217497, 134217493, 134217487, 134217467, 134217439, 134217437, 134217409, 134217403,
134217401, 134217367, 134217361, 134217353, 134217323, 134217301, 134217277, 134217257,
@ -407,7 +420,7 @@ const PRIMES27: &'static [u64] = &[
134216737, 134216729,
];
const PRIMES28: &'static [u64] = &[
const PRIMES28: &[u64] = &[
268435399, 268435367, 268435361, 268435337, 268435331, 268435313, 268435291, 268435273,
268435243, 268435183, 268435171, 268435157, 268435147, 268435133, 268435129, 268435121,
268435109, 268435091, 268435067, 268435043, 268435039, 268435033, 268435019, 268435009,
@ -418,7 +431,7 @@ const PRIMES28: &'static [u64] = &[
268434479, 268434461,
];
const PRIMES29: &'static [u64] = &[
const PRIMES29: &[u64] = &[
536870909, 536870879, 536870869, 536870849, 536870839, 536870837, 536870819, 536870813,
536870791, 536870779, 536870767, 536870743, 536870729, 536870723, 536870717, 536870701,
536870683, 536870657, 536870641, 536870627, 536870611, 536870603, 536870599, 536870573,
@ -428,7 +441,7 @@ const PRIMES29: &'static [u64] = &[
536870027, 536869999, 536869951, 536869943, 536869937, 536869919, 536869901, 536869891,
];
const PRIMES30: &'static [u64] = &[
const PRIMES30: &[u64] = &[
1073741789, 1073741783, 1073741741, 1073741723, 1073741719, 1073741717, 1073741689, 1073741671,
1073741663, 1073741651, 1073741621, 1073741567, 1073741561, 1073741527, 1073741503, 1073741477,
1073741467, 1073741441, 1073741419, 1073741399, 1073741387, 1073741381, 1073741371, 1073741329,
@ -437,7 +450,7 @@ const PRIMES30: &'static [u64] = &[
1073740853, 1073740847, 1073740819, 1073740807,
];
const PRIMES31: &'static [u64] = &[
const PRIMES31: &[u64] = &[
2147483647, 2147483629, 2147483587, 2147483579, 2147483563, 2147483549, 2147483543, 2147483497,
2147483489, 2147483477, 2147483423, 2147483399, 2147483353, 2147483323, 2147483269, 2147483249,
2147483237, 2147483179, 2147483171, 2147483137, 2147483123, 2147483077, 2147483069, 2147483059,
@ -446,7 +459,7 @@ const PRIMES31: &'static [u64] = &[
2147482763, 2147482739, 2147482697, 2147482693, 2147482681, 2147482663, 2147482661,
];
const PRIMES32: &'static [u64] = &[
const PRIMES32: &[u64] = &[
4294967291, 4294967279, 4294967231, 4294967197, 4294967189, 4294967161, 4294967143, 4294967111,
4294967087, 4294967029, 4294966997, 4294966981, 4294966943, 4294966927, 4294966909, 4294966877,
4294966829, 4294966813, 4294966769, 4294966667, 4294966661, 4294966657, 4294966651, 4294966639,
@ -454,7 +467,7 @@ const PRIMES32: &'static [u64] = &[
4294966373, 4294966367, 4294966337, 4294966297,
];
const PRIMES33: &'static [u64] = &[
const PRIMES33: &[u64] = &[
8589934583, 8589934567, 8589934543, 8589934513, 8589934487, 8589934307, 8589934291, 8589934289,
8589934271, 8589934237, 8589934211, 8589934207, 8589934201, 8589934187, 8589934151, 8589934141,
8589934139, 8589934117, 8589934103, 8589934099, 8589934091, 8589934069, 8589934049, 8589934027,
@ -463,7 +476,7 @@ const PRIMES33: &'static [u64] = &[
8589933647, 8589933641, 8589933637, 8589933631, 8589933629, 8589933619, 8589933601, 8589933581,
];
const PRIMES34: &'static [u64] = &[
const PRIMES34: &[u64] = &[
17179869143,
17179869107,
17179869071,
@ -514,7 +527,7 @@ const PRIMES34: &'static [u64] = &[
17179868183,
];
const PRIMES35: &'static [u64] = &[
const PRIMES35: &[u64] = &[
34359738337,
34359738319,
34359738307,
@ -547,7 +560,7 @@ const PRIMES35: &'static [u64] = &[
34359737371,
];
const PRIMES36: &'static [u64] = &[
const PRIMES36: &[u64] = &[
68719476731,
68719476719,
68719476713,
@ -599,7 +612,7 @@ const PRIMES36: &'static [u64] = &[
68719475729,
];
const PRIMES37: &'static [u64] = &[
const PRIMES37: &[u64] = &[
137438953447,
137438953441,
137438953427,
@ -625,7 +638,7 @@ const PRIMES37: &'static [u64] = &[
137438952491,
];
const PRIMES38: &'static [u64] = &[
const PRIMES38: &[u64] = &[
274877906899,
274877906857,
274877906837,
@ -665,7 +678,7 @@ const PRIMES38: &'static [u64] = &[
274877905931,
];
const PRIMES39: &'static [u64] = &[
const PRIMES39: &[u64] = &[
549755813881,
549755813869,
549755813821,
@ -711,7 +724,7 @@ const PRIMES39: &'static [u64] = &[
549755812867,
];
const PRIMES40: &'static [u64] = &[
const PRIMES40: &[u64] = &[
1099511627689,
1099511627609,
1099511627581,
@ -741,7 +754,7 @@ const PRIMES40: &'static [u64] = &[
1099511626771,
];
const PRIMES41: &'static [u64] = &[
const PRIMES41: &[u64] = &[
2199023255531,
2199023255521,
2199023255497,
@ -780,7 +793,7 @@ const PRIMES41: &'static [u64] = &[
2199023254567,
];
const PRIMES42: &'static [u64] = &[
const PRIMES42: &[u64] = &[
4398046511093,
4398046511087,
4398046511071,
@ -820,7 +833,7 @@ const PRIMES42: &'static [u64] = &[
4398046510093,
];
const PRIMES43: &'static [u64] = &[
const PRIMES43: &[u64] = &[
8796093022151,
8796093022141,
8796093022091,
@ -855,7 +868,7 @@ const PRIMES43: &'static [u64] = &[
8796093021269,
];
const PRIMES44: &'static [u64] = &[
const PRIMES44: &[u64] = &[
17592186044399,
17592186044299,
17592186044297,
@ -888,7 +901,7 @@ const PRIMES44: &'static [u64] = &[
17592186043409,
];
const PRIMES45: &'static [u64] = &[
const PRIMES45: &[u64] = &[
35184372088777,
35184372088763,
35184372088751,
@ -929,7 +942,7 @@ const PRIMES45: &'static [u64] = &[
35184372087869,
];
const PRIMES46: &'static [u64] = &[
const PRIMES46: &[u64] = &[
70368744177643,
70368744177607,
70368744177601,
@ -964,7 +977,7 @@ const PRIMES46: &'static [u64] = &[
70368744176711,
];
const PRIMES47: &'static [u64] = &[
const PRIMES47: &[u64] = &[
140737488355213,
140737488355201,
140737488355181,
@ -986,7 +999,7 @@ const PRIMES47: &'static [u64] = &[
140737488354329,
];
const PRIMES48: &'static [u64] = &[
const PRIMES48: &[u64] = &[
281474976710597,
281474976710591,
281474976710567,
@ -1019,7 +1032,7 @@ const PRIMES48: &'static [u64] = &[
281474976709637,
];
const PRIMES49: &'static [u64] = &[
const PRIMES49: &[u64] = &[
562949953421231,
562949953421201,
562949953421189,
@ -1053,7 +1066,7 @@ const PRIMES49: &'static [u64] = &[
562949953420297,
];
const PRIMES50: &'static [u64] = &[
const PRIMES50: &[u64] = &[
1125899906842597,
1125899906842589,
1125899906842573,

View file

@ -1,6 +1,6 @@
use crate::common::util::*;
static INPUT: &'static str = "lorem_ipsum.txt";
static INPUT: &str = "lorem_ipsum.txt";
#[test]
fn test_stdin_default() {

View file

@ -116,11 +116,11 @@ fn test_install_ancestors_mode_directories() {
assert!(at.dir_exists(ancestor2));
assert!(at.dir_exists(target_dir));
assert_ne!(0o40700 as u32, at.metadata(ancestor1).permissions().mode());
assert_ne!(0o40700 as u32, at.metadata(ancestor2).permissions().mode());
assert_ne!(0o40_700_u32, at.metadata(ancestor1).permissions().mode());
assert_ne!(0o40_700_u32, at.metadata(ancestor2).permissions().mode());
// Expected mode only on the target_dir.
assert_eq!(0o40700 as u32, at.metadata(target_dir).permissions().mode());
assert_eq!(0o40_700_u32, at.metadata(target_dir).permissions().mode());
}
#[test]
@ -184,7 +184,7 @@ fn test_install_mode_numeric() {
assert!(at.file_exists(file));
assert!(at.file_exists(dest_file));
let permissions = at.metadata(dest_file).permissions();
assert_eq!(0o100333 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o100_333_u32, PermissionsExt::mode(&permissions));
let mode_arg = "-m 0333";
at.mkdir(dir2);
@ -195,7 +195,7 @@ fn test_install_mode_numeric() {
assert!(at.file_exists(file));
assert!(at.file_exists(dest_file));
let permissions = at.metadata(dest_file).permissions();
assert_eq!(0o100333 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o100_333_u32, PermissionsExt::mode(&permissions));
}
#[test]
@ -213,7 +213,7 @@ fn test_install_mode_symbolic() {
assert!(at.file_exists(file));
assert!(at.file_exists(dest_file));
let permissions = at.metadata(dest_file).permissions();
assert_eq!(0o100003 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o100_003_u32, PermissionsExt::mode(&permissions));
}
#[test]
@ -251,7 +251,7 @@ fn test_install_mode_directories() {
assert!(at.dir_exists(component));
let permissions = at.metadata(component).permissions();
assert_eq!(0o040333 as u32, PermissionsExt::mode(&permissions));
assert_eq!(0o040_333_u32, PermissionsExt::mode(&permissions));
}
#[test]

View file

@ -51,6 +51,7 @@ fn test_ls_a() {
.unwrap(),
);
#[allow(clippy::trivial_regex)]
let re_pwd = Regex::new(r"^\.\n").unwrap();
// Using the present working directory
@ -124,7 +125,7 @@ fn test_ls_width() {
for option in &["-w 100", "-w=100", "--width=100", "--width 100"] {
scene
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.args(&option.split(' ').collect::<Vec<_>>())
.succeeds()
.stdout_only("test-width-1 test-width-2 test-width-3 test-width-4\n");
}
@ -132,7 +133,7 @@ fn test_ls_width() {
for option in &["-w 50", "-w=50", "--width=50", "--width 50"] {
scene
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.args(&option.split(' ').collect::<Vec<_>>())
.succeeds()
.stdout_only("test-width-1 test-width-3\ntest-width-2 test-width-4\n");
}
@ -149,7 +150,7 @@ fn test_ls_width() {
] {
scene
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.args(&option.split(' ').collect::<Vec<_>>())
.succeeds()
.stdout_only("test-width-1\ntest-width-2\ntest-width-3\ntest-width-4\n");
}
@ -163,7 +164,7 @@ fn test_ls_width() {
for option in &["-w 1a", "-w=1a", "--width=1a", "--width 1a"] {
scene
.ucmd()
.args(&option.split(" ").collect::<Vec<_>>())
.args(&option.split(' ').collect::<Vec<_>>())
.fails()
.stderr_only("ls: invalid line width: 1a");
}
@ -417,7 +418,7 @@ fn test_ls_long_formats() {
] {
let result = scene
.ucmd()
.args(&arg.split(" ").collect::<Vec<_>>())
.args(&arg.split(' ').collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
assert!(re_two.is_match(result.stdout_str()));
@ -427,7 +428,7 @@ fn test_ls_long_formats() {
let result = scene
.ucmd()
.arg("-n")
.args(&arg.split(" ").collect::<Vec<_>>())
.args(&arg.split(' ').collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
assert!(re_two_num.is_match(result.stdout_str()));
@ -446,7 +447,7 @@ fn test_ls_long_formats() {
] {
let result = scene
.ucmd()
.args(&arg.split(" ").collect::<Vec<_>>())
.args(&arg.split(' ').collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
assert!(re_one.is_match(result.stdout_str()));
@ -456,7 +457,7 @@ fn test_ls_long_formats() {
let result = scene
.ucmd()
.arg("-n")
.args(&arg.split(" ").collect::<Vec<_>>())
.args(&arg.split(' ').collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
assert!(re_one_num.is_match(result.stdout_str()));
@ -478,7 +479,7 @@ fn test_ls_long_formats() {
] {
let result = scene
.ucmd()
.args(&arg.split(" ").collect::<Vec<_>>())
.args(&arg.split(' ').collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
assert!(re_zero.is_match(result.stdout_str()));
@ -488,7 +489,7 @@ fn test_ls_long_formats() {
let result = scene
.ucmd()
.arg("-n")
.args(&arg.split(" ").collect::<Vec<_>>())
.args(&arg.split(' ').collect::<Vec<_>>())
.arg("test-long-formats")
.succeeds();
assert!(re_zero.is_match(result.stdout_str()));
@ -898,6 +899,12 @@ fn test_ls_recursive() {
scene.ucmd().arg("a").succeeds();
scene.ucmd().arg("a/a").succeeds();
scene
.ucmd()
.arg("z")
.arg("-R")
.succeeds()
.stdout_contains(&"z:");
let result = scene
.ucmd()
.arg("--color=never")
@ -1063,7 +1070,7 @@ fn test_ls_indicator_style() {
for opt in options {
scene
.ucmd()
.arg(format!("{}", opt))
.arg(opt.to_string())
.succeeds()
.stdout_contains(&"/");
}
@ -1085,7 +1092,10 @@ fn test_ls_indicator_style() {
{
use self::unix_socket::UnixListener;
let dir = tempfile::Builder::new().prefix("unix_socket").tempdir().expect("failed to create dir");
let dir = tempfile::Builder::new()
.prefix("unix_socket")
.tempdir()
.expect("failed to create dir");
let socket_path = dir.path().join("sock");
let _listener = UnixListener::bind(&socket_path).expect("failed to create socket");

View file

@ -1,12 +1,12 @@
use crate::common::util::*;
static TEST_DIR1: &'static str = "mkdir_test1";
static TEST_DIR2: &'static str = "mkdir_test2";
static TEST_DIR3: &'static str = "mkdir_test3";
static TEST_DIR4: &'static str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &'static str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &'static str = "mkdir_test6";
static TEST_FILE7: &'static str = "mkdir_test7";
static TEST_DIR1: &str = "mkdir_test1";
static TEST_DIR2: &str = "mkdir_test2";
static TEST_DIR3: &str = "mkdir_test3";
static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &str = "mkdir_test6";
static TEST_FILE7: &str = "mkdir_test7";
#[test]
fn test_mkdir_mkdir() {

View file

@ -3,19 +3,19 @@ use crate::common::util::*;
use std::path::PathBuf;
use tempfile::tempdir;
static TEST_TEMPLATE1: &'static str = "tempXXXXXX";
static TEST_TEMPLATE2: &'static str = "temp";
static TEST_TEMPLATE3: &'static str = "tempX";
static TEST_TEMPLATE4: &'static str = "tempXX";
static TEST_TEMPLATE5: &'static str = "tempXXX";
static TEST_TEMPLATE6: &'static str = "tempXXXlate";
static TEST_TEMPLATE7: &'static str = "XXXtemplate";
static TEST_TEMPLATE1: &str = "tempXXXXXX";
static TEST_TEMPLATE2: &str = "temp";
static TEST_TEMPLATE3: &str = "tempX";
static TEST_TEMPLATE4: &str = "tempXX";
static TEST_TEMPLATE5: &str = "tempXXX";
static TEST_TEMPLATE6: &str = "tempXXXlate";
static TEST_TEMPLATE7: &str = "XXXtemplate";
#[cfg(unix)]
static TEST_TEMPLATE8: &'static str = "tempXXXl/ate";
static TEST_TEMPLATE8: &str = "tempXXXl/ate";
#[cfg(windows)]
static TEST_TEMPLATE8: &'static str = "tempXXXl\\ate";
static TEST_TEMPLATE8: &str = "tempXXXl\\ate";
const TMPDIR: &'static str = "TMPDIR";
const TMPDIR: &str = "TMPDIR";
#[test]
fn test_mktemp_mktemp() {

View file

@ -82,7 +82,7 @@ fn test_mv_strip_slashes() {
let dir = "test_mv_strip_slashes_dir";
let file = "test_mv_strip_slashes_file";
let mut source = file.to_owned();
source.push_str("/");
source.push('/');
at.mkdir(dir);
at.touch(file);

View file

@ -9,7 +9,7 @@ use std::io::Write;
use std::path::Path;
// octal dump of 'abcdefghijklmnopqrstuvwxyz\n'
static ALPHA_OUT: &'static str = "
static ALPHA_OUT: &str = "
0000000 061141 062143 063145 064147 065151 066153 067155 070157
0000020 071161 072163 073165 074167 075171 000012
0000033
@ -563,7 +563,7 @@ fn test_dec_offset() {
#[test]
fn test_no_offset() {
let input = [0u8; 31];
const LINE: &'static str = " 00000000 00000000 00000000 00000000\n";
const LINE: &str = " 00000000 00000000 00000000 00000000\n";
let expected_output = [LINE, LINE, LINE, LINE].join("");
new_ucmd!()

View file

@ -7,7 +7,7 @@ struct TestData<'b> {
out: &'b str,
}
static EXAMPLE_DATA: &'static [TestData<'static>] = &[
static EXAMPLE_DATA: &[TestData] = &[
// Ensure that paste properly handles files lacking a final newline.
TestData {
name: "no-nl-1",
@ -64,8 +64,8 @@ static EXAMPLE_DATA: &'static [TestData<'static>] = &[
#[test]
fn test_combine_pairs_of_lines() {
for s in vec!["-s", "--serial"] {
for d in vec!["-d", "--delimiters"] {
for &s in &["-s", "--serial"] {
for &d in &["-d", "--delimiters"] {
new_ucmd!()
.args(&[s, d, "\t\n", "html_colors.txt"])
.run()
@ -76,7 +76,7 @@ fn test_combine_pairs_of_lines() {
#[test]
fn test_multi_stdin() {
for d in vec!["-d", "--delimiters"] {
for &d in &["-d", "--delimiters"] {
new_ucmd!()
.args(&[d, "\t\n", "-", "-"])
.pipe_in_fixture("html_colors.txt")

View file

@ -1,6 +1,6 @@
use crate::common::util::*;
static GIBBERISH: &'static str = "supercalifragilisticexpialidocious";
static GIBBERISH: &str = "supercalifragilisticexpialidocious";
#[test]
fn test_canonicalize() {

View file

@ -61,6 +61,7 @@ const TESTS: [TestCase; 10] = [
},
];
#[allow(clippy::needless_lifetimes)]
fn convert_path<'a>(path: &'a str) -> Cow<'a, str> {
#[cfg(windows)]
return path.replace("/", "\\").into();

View file

@ -14,11 +14,11 @@ fn test_output_is_random_permutation() {
let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n")
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort();
result_seq.sort_unstable();
assert_ne!(result.stdout_str(), input, "Output is not randomised");
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
@ -31,11 +31,11 @@ fn test_zero_termination() {
let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\0")
.split('\0')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort();
result_seq.sort_unstable();
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
@ -55,11 +55,11 @@ fn test_echo() {
let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n")
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort();
result_seq.sort_unstable();
assert_eq!(result_seq, input_seq, "Output is not a permutation");
}
@ -81,11 +81,11 @@ fn test_head_count() {
let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n")
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort();
result_seq.sort_unstable();
assert_eq!(result_seq.len(), repeat_limit, "Output is not limited");
assert!(
result_seq.iter().all(|x| input_seq.contains(x)),
@ -113,7 +113,7 @@ fn test_repeat() {
let result_seq: Vec<i32> = result
.stdout_str()
.split("\n")
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
@ -141,11 +141,11 @@ fn test_file_input() {
let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n")
.split('\n')
.filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap())
.collect();
result_seq.sort();
result_seq.sort_unstable();
assert_eq!(result_seq, expected_seq, "Output is not a permutation");
}

View file

@ -4,14 +4,14 @@ fn test_helper(file_name: &str, possible_args: &[&str]) {
for args in possible_args {
new_ucmd!()
.arg(format!("{}.txt", file_name))
.args(&args.split(' ').collect::<Vec<&str>>())
.args(&args.split_whitespace().collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected", file_name));
new_ucmd!()
.arg(format!("{}.txt", file_name))
.arg("--debug")
.args(&args.split(' ').collect::<Vec<&str>>())
.args(&args.split_whitespace().collect::<Vec<&str>>())
.succeeds()
.stdout_is_fixture(format!("{}.expected.debug", file_name));
}
@ -288,7 +288,7 @@ fn test_dictionary_order() {
#[test]
fn test_dictionary_order2() {
for non_dictionary_order2_param in vec!["-d"] {
for non_dictionary_order2_param in &["-d"] {
new_ucmd!()
.pipe_in("a👦🏻aa b\naaaa b")
.arg(non_dictionary_order2_param)
@ -299,7 +299,7 @@ fn test_dictionary_order2() {
#[test]
fn test_non_printing_chars() {
for non_printing_chars_param in vec!["-i"] {
for non_printing_chars_param in &["-i"] {
new_ucmd!()
.pipe_in("a👦🏻aa\naaaa")
.arg(non_printing_chars_param)
@ -361,7 +361,7 @@ fn test_mixed_floats_ints_chars_numeric_stable() {
#[test]
fn test_numeric_floats_and_ints2() {
for numeric_sort_param in vec!["-n", "--numeric-sort"] {
for numeric_sort_param in &["-n", "--numeric-sort"] {
let input = "1.444\n8.013\n1\n-8\n1.04\n-1";
new_ucmd!()
.arg(numeric_sort_param)
@ -373,7 +373,7 @@ fn test_numeric_floats_and_ints2() {
#[test]
fn test_numeric_floats2() {
for numeric_sort_param in vec!["-n", "--numeric-sort"] {
for numeric_sort_param in &["-n", "--numeric-sort"] {
let input = "1.444\n8.013\n1.58590\n-8.90880\n1.040000000\n-.05";
new_ucmd!()
.arg(numeric_sort_param)
@ -426,7 +426,7 @@ fn test_default_unsorted_ints2() {
#[test]
fn test_numeric_unique_ints2() {
for numeric_unique_sort_param in vec!["-nu"] {
for numeric_unique_sort_param in &["-nu"] {
let input = "9\n9\n8\n1\n";
new_ucmd!()
.arg(numeric_unique_sort_param)
@ -471,7 +471,7 @@ fn test_keys_invalid_field() {
new_ucmd!()
.args(&["-k", "1."])
.fails()
.stderr_only("sort: failed to parse character index for key `1.`: cannot parse integer from empty string");
.stderr_only("sort: failed to parse key `1.`: failed to parse character index ``: cannot parse integer from empty string");
}
#[test]
@ -479,7 +479,7 @@ fn test_keys_invalid_field_option() {
new_ucmd!()
.args(&["-k", "1.1x"])
.fails()
.stderr_only("sort: invalid option for key: `x`");
.stderr_only("sort: failed to parse key `1.1x`: invalid option: `x`");
}
#[test]
@ -487,7 +487,7 @@ fn test_keys_invalid_field_zero() {
new_ucmd!()
.args(&["-k", "0.1"])
.fails()
.stderr_only("sort: field index was 0");
.stderr_only("sort: failed to parse key `0.1`: field index can not be 0");
}
#[test]
@ -495,7 +495,7 @@ fn test_keys_invalid_char_zero() {
new_ucmd!()
.args(&["-k", "1.0"])
.fails()
.stderr_only("sort: invalid character index 0 in `1.0` for the start position of a field");
.stderr_only("sort: failed to parse key `1.0`: invalid character index 0 for the start position of a field");
}
#[test]
@ -526,6 +526,11 @@ fn test_keys_with_options_blanks_start() {
}
}
#[test]
fn test_keys_blanks_with_char_idx() {
test_helper("keys_blanks", &["-k 1.2b"])
}
#[test]
fn test_keys_with_options_blanks_end() {
let input = "a b
@ -574,6 +579,54 @@ aaaa
.stdout_only(input);
}
#[test]
fn test_keys_negative_size_match() {
// If the end of a field is before its start, we should not crash.
// Debug output should report "no match for key" at the start position (i.e. the later position).
test_helper("keys_negative_size", &["-k 3,1"]);
}
#[test]
fn test_keys_ignore_flag() {
test_helper("keys_ignore_flag", &["-k 1n -b"])
}
#[test]
fn test_doesnt_inherit_key_settings() {
let input = " 1
2
10
";
new_ucmd!()
.args(&["-k", "1b", "-n"])
.pipe_in(input)
.succeeds()
.stdout_only(
" 1
10
2
",
);
}
#[test]
fn test_inherits_key_settings() {
let input = " 1
2
10
";
new_ucmd!()
.args(&["-k", "1", "-n"])
.pipe_in(input)
.succeeds()
.stdout_only(
" 1
2
10
",
);
}
#[test]
fn test_zero_terminated() {
test_helper("zero-terminated", &["-z"]);
@ -695,10 +748,9 @@ fn test_dictionary_and_nonprinting_conflicts() {
.succeeds();
}
for conflicting_arg in &conflicting_args {
// FIXME: this should ideally fail.
new_ucmd!()
.args(&["-k", &format!("1{},1{}", restricted_arg, conflicting_arg)])
.succeeds();
.fails();
}
}
}
@ -711,3 +763,22 @@ fn test_trailing_separator() {
.succeeds()
.stdout_is("aax\naaa\n");
}
#[test]
fn test_nonexistent_file() {
new_ucmd!()
.arg("nonexistent.txt")
.fails()
.status_code(2)
.stderr_only(
#[cfg(not(windows))]
"sort: cannot read: \"nonexistent.txt\": No such file or directory (os error 2)",
#[cfg(windows)]
"sort: cannot read: \"nonexistent.txt\": The system cannot find the file specified. (os error 2)",
);
}
#[test]
fn test_blanks() {
test_helper("blanks", &["-b", "--ignore-blanks"]);
}

View file

@ -98,7 +98,7 @@ impl RandomFile {
let to_write = std::cmp::min(remaining_size, buffer.len());
let buf = &mut buffer[..to_write];
rng.fill(buf);
writer.write(buf).unwrap();
writer.write_all(buf).unwrap();
remaining_size -= to_write;
}
@ -179,6 +179,7 @@ fn test_split_bytes_prime_part_size() {
let mut fns = glob.collect();
// glob.collect() is not guaranteed to return in sorted order, so we sort.
fns.sort();
#[allow(clippy::needless_range_loop)]
for i in 0..5 {
assert_eq!(glob.directory.metadata(&fns[i]).len(), 1753);
}
@ -246,9 +247,9 @@ fn test_filter() {
assert!(
glob.collate().iter().find(|&&c| {
// is not i
c != ('i' as u8)
c != (b'i')
// is not newline
&& c != ('\n' as u8)
&& c != (b'\n')
}) == None
);
}
@ -271,7 +272,7 @@ fn test_filter_with_env_var_set() {
let glob = Glob::new(&at, ".", r"x[[:alpha:]][[:alpha:]]$");
assert_eq!(glob.collate(), at.read_bytes(name));
assert!(env::var("FILE").unwrap_or("var was unset".to_owned()) == env_var_value);
assert!(env::var("FILE").unwrap_or_else(|_| "var was unset".to_owned()) == env_var_value);
}
#[test]

View file

@ -97,13 +97,13 @@ fn test_invalid_option() {
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
const NORMAL_FMTSTR: &'static str =
const NORMAL_FMTSTR: &str =
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s %u %U %x %X %y %Y %z %Z"; // avoid "%w %W" (birth/creation) due to `stat` limitations and linux kernel & rust version capability variations
#[cfg(any(target_os = "linux"))]
const DEV_FMTSTR: &'static str =
const DEV_FMTSTR: &str =
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (%t/%T) %u %U %w %W %x %X %y %Y %z %Z";
#[cfg(target_os = "linux")]
const FS_FMTSTR: &'static str = "%b %c %i %l %n %s %S %t %T"; // avoid "%a %d %f" which can cause test failure due to race conditions
const FS_FMTSTR: &str = "%b %c %i %l %n %s %S %t %T"; // avoid "%a %d %f" which can cause test failure due to race conditions
#[test]
#[cfg(target_os = "linux")]
@ -140,7 +140,7 @@ fn test_terse_normal_format() {
assert!(!v_expect.is_empty());
// uu_stat does not support selinux
if v_actual.len() == v_expect.len() - 1 && v_expect[v_expect.len() - 1].contains(":") {
if v_actual.len() == v_expect.len() - 1 && v_expect[v_expect.len() - 1].contains(':') {
// assume last element contains: `SELinux security context string`
v_expect.pop();
}
@ -222,7 +222,7 @@ fn test_symlinks() {
let mut tested: bool = false;
// arbitrarily chosen symlinks with hope that the CI environment provides at least one of them
for file in vec![
for file in &[
"/bin/sh",
"/bin/sudoedit",
"/usr/bin/ex",

View file

@ -5,9 +5,9 @@ use crate::common::util::*;
use std::char::from_digit;
use std::io::Write;
static FOOBAR_TXT: &'static str = "foobar.txt";
static FOOBAR_2_TXT: &'static str = "foobar2.txt";
static FOOBAR_WITH_NULL_TXT: &'static str = "foobar_with_null.txt";
static FOOBAR_TXT: &str = "foobar.txt";
static FOOBAR_2_TXT: &str = "foobar2.txt";
static FOOBAR_WITH_NULL_TXT: &str = "foobar_with_null.txt";
#[test]
fn test_stdin_default() {
@ -153,8 +153,8 @@ fn test_follow_with_pid() {
#[test]
fn test_single_big_args() {
const FILE: &'static str = "single_big_args.txt";
const EXPECTED_FILE: &'static str = "single_big_args_expected.txt";
const FILE: &str = "single_big_args.txt";
const EXPECTED_FILE: &str = "single_big_args_expected.txt";
const LINES: usize = 1_000_000;
const N_ARG: usize = 100_000;
@ -162,13 +162,13 @@ fn test_single_big_args() {
let mut big_input = at.make_file(FILE);
for i in 0..LINES {
write!(&mut big_input, "Line {}\n", i).expect("Could not write to FILE");
writeln!(&mut big_input, "Line {}", i).expect("Could not write to FILE");
}
big_input.flush().expect("Could not flush FILE");
let mut big_expected = at.make_file(EXPECTED_FILE);
for i in (LINES - N_ARG)..LINES {
write!(&mut big_expected, "Line {}\n", i).expect("Could not write to EXPECTED_FILE");
writeln!(&mut big_expected, "Line {}", i).expect("Could not write to EXPECTED_FILE");
}
big_expected.flush().expect("Could not flush EXPECTED_FILE");
@ -201,8 +201,8 @@ fn test_bytes_stdin() {
#[test]
fn test_bytes_big() {
const FILE: &'static str = "test_bytes_big.txt";
const EXPECTED_FILE: &'static str = "test_bytes_big_expected.txt";
const FILE: &str = "test_bytes_big.txt";
const EXPECTED_FILE: &str = "test_bytes_big_expected.txt";
const BYTES: usize = 1_000_000;
const N_ARG: usize = 100_000;
@ -257,10 +257,10 @@ fn test_parse_size() {
for &(c, exp) in &suffixes {
let s = format!("2{}B", c);
assert_eq!(Ok(2 * (1000 as u64).pow(exp)), parse_size(&s));
assert_eq!(Ok(2 * (1000_u64).pow(exp)), parse_size(&s));
let s = format!("2{}", c);
assert_eq!(Ok(2 * (1024 as u64).pow(exp)), parse_size(&s));
assert_eq!(Ok(2 * (1024_u64).pow(exp)), parse_size(&s));
}
// Sizes that are too big.
@ -273,8 +273,8 @@ fn test_parse_size() {
#[test]
fn test_lines_with_size_suffix() {
const FILE: &'static str = "test_lines_with_size_suffix.txt";
const EXPECTED_FILE: &'static str = "test_lines_with_size_suffix_expected.txt";
const FILE: &str = "test_lines_with_size_suffix.txt";
const EXPECTED_FILE: &str = "test_lines_with_size_suffix_expected.txt";
const LINES: usize = 3_000;
const N_ARG: usize = 2 * 1024;

View file

@ -401,8 +401,8 @@ fn get_dstswitch_hour() -> Option<String> {
for _i in 0..(366 * 24) {
if is_dst_switch_hour(ts) {
let mut tm = time::at(ts);
tm.tm_hour = tm.tm_hour + 1;
let s = time::strftime("%Y%m%d%H%M", &tm).unwrap().to_string();
tm.tm_hour += 1;
let s = time::strftime("%Y%m%d%H%M", &tm).unwrap();
return Some(s);
}
ts = ts + time::Duration::hours(1);
@ -415,10 +415,7 @@ fn test_touch_mtime_dst_fails() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_mtime_dst_fails";
match get_dstswitch_hour() {
Some(s) => {
ucmd.args(&["-m", "-t", &s, file]).fails();
}
None => (),
if let Some(s) = get_dstswitch_hour() {
ucmd.args(&["-m", "-t", &s, file]).fails();
}
}

View file

@ -1,8 +1,8 @@
use crate::common::util::*;
use std::io::{Seek, SeekFrom, Write};
static TFILE1: &'static str = "truncate_test_1";
static TFILE2: &'static str = "truncate_test_2";
static TFILE1: &str = "truncate_test_1";
static TFILE2: &str = "truncate_test_2";
#[test]
fn test_increase_file_size() {
@ -262,3 +262,11 @@ fn test_reference_file_not_found() {
.fails()
.stderr_contains("cannot stat 'a': No such file or directory");
}
#[test]
fn test_reference_with_size_file_not_found() {
new_ucmd!()
.args(&["-r", "a", "-s", "+1", "b"])
.fails()
.stderr_contains("cannot stat 'a': No such file or directory");
}

View file

@ -1,10 +1,10 @@
use crate::common::util::*;
static INPUT: &'static str = "sorted.txt";
static OUTPUT: &'static str = "sorted-output.txt";
static SKIP_CHARS: &'static str = "skip-chars.txt";
static SKIP_FIELDS: &'static str = "skip-fields.txt";
static SORTED_ZERO_TERMINATED: &'static str = "sorted-zero-terminated.txt";
static INPUT: &str = "sorted.txt";
static OUTPUT: &str = "sorted-output.txt";
static SKIP_CHARS: &str = "skip-chars.txt";
static SKIP_FIELDS: &str = "skip-fields.txt";
static SORTED_ZERO_TERMINATED: &str = "sorted-zero-terminated.txt";
#[test]
fn test_stdin_default() {

View file

@ -1,27 +1,23 @@
use crate::common::util::*;
use std::env;
#[test]
fn test_users_noarg() {
new_ucmd!().succeeds();
}
#[test]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn test_users_check_name() {
let result = TestScenario::new(util_name!()).ucmd_keepenv().succeeds();
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
// Expectation: USER is often set
let key = "USER";
let expected = TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LANGUAGE", "C")
.succeeds()
.stdout_move_str();
match env::var(key) {
Err(e) => println!("Key {} isn't set. Found {}", &key, e),
Ok(username) =>
// Check if "users" contains the name of the user
{
println!("username found {}", &username);
// println!("result.stdout {}", &result.stdout);
if !result.stdout_str().is_empty() {
result.stdout_contains(&username);
}
}
}
new_ucmd!().succeeds().stdout_is(&expected);
}

View file

@ -3,7 +3,7 @@ use crate::common::util::*;
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_count() {
for opt in vec!["-q", "--count"] {
for opt in &["-q", "--count"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -14,7 +14,7 @@ fn test_count() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_boot() {
for opt in vec!["-b", "--boot"] {
for opt in &["-b", "--boot"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -25,7 +25,7 @@ fn test_boot() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_heading() {
for opt in vec!["-H", "--heading"] {
for opt in &["-H", "--heading"] {
// allow whitespace variation
// * minor whitespace differences occur between platform built-in outputs;
// specifically number of TABs between "TIME" and "COMMENT" may be variant
@ -42,7 +42,7 @@ fn test_heading() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_short() {
for opt in vec!["-s", "--short"] {
for opt in &["-s", "--short"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -53,7 +53,7 @@ fn test_short() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_login() {
for opt in vec!["-l", "--login"] {
for opt in &["-l", "--login"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -64,7 +64,7 @@ fn test_login() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_m() {
for opt in vec!["-m"] {
for opt in &["-m"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -75,7 +75,7 @@ fn test_m() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_process() {
for opt in vec!["-p", "--process"] {
for opt in &["-p", "--process"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -85,7 +85,7 @@ fn test_process() {
#[test]
fn test_runlevel() {
for opt in vec!["-r", "--runlevel"] {
for opt in &["-r", "--runlevel"] {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
new_ucmd!()
.arg(opt)
@ -100,7 +100,7 @@ fn test_runlevel() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_time() {
for opt in vec!["-t", "--time"] {
for opt in &["-t", "--time"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -117,7 +117,7 @@ fn test_mesg() {
// same as -T
// --writable
// same as -T
for opt in vec!["-T", "-w", "--mesg", "--message", "--writable"] {
for opt in &["-T", "-w", "--mesg", "--message", "--writable"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -147,7 +147,7 @@ fn test_too_many_args() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_users() {
for opt in vec!["-u", "--users"] {
for opt in &["-u", "--users"] {
let actual = new_ucmd!().arg(opt).succeeds().stdout_move_str();
let expect = expected_result(&[opt]);
println!("actual: {:?}", actual);
@ -172,18 +172,17 @@ fn test_users() {
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_lookup() {
for opt in vec!["--lookup"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
let opt = "--lookup";
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(&[opt]));
}
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_dead() {
for opt in vec!["-d", "--dead"] {
for opt in &["-d", "--dead"] {
new_ucmd!()
.arg(opt)
.succeeds()
@ -222,7 +221,7 @@ fn test_all() {
return;
}
for opt in vec!["-a", "--all"] {
for opt in &["-a", "--all"] {
new_ucmd!()
.arg(opt)
.succeeds()

View file

@ -1,7 +1,4 @@
#![allow(dead_code)]
#[cfg(not(windows))]
use libc;
use pretty_assertions::assert_eq;
use std::env;
#[cfg(not(windows))]
@ -16,7 +13,6 @@ use std::os::windows::fs::{symlink_dir, symlink_file};
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};
use std::rc::Rc;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;
use tempfile::TempDir;
@ -40,7 +36,7 @@ static NO_STDIN_MEANINGLESS: &str = "Setting this flag has no effect if there is
/// Test if the program is running under CI
pub fn is_ci() -> bool {
std::env::var("CI")
.unwrap_or(String::from("false"))
.unwrap_or_else(|_| String::from("false"))
.eq_ignore_ascii_case("true")
}
@ -62,54 +58,54 @@ pub struct CmdResult {
/// see [`success`]
success: bool,
/// captured standard output after running the Command
stdout: String,
stdout: Vec<u8>,
/// captured standard error after running the Command
stderr: String,
stderr: Vec<u8>,
}
impl CmdResult {
/// Returns a reference to the program's standard output as a slice of bytes
pub fn stdout(&self) -> &[u8] {
&self.stdout.as_bytes()
&self.stdout
}
/// Returns the program's standard output as a string slice
pub fn stdout_str(&self) -> &str {
&self.stdout
std::str::from_utf8(&self.stdout).unwrap()
}
/// Returns the program's standard output as a string
/// consumes self
pub fn stdout_move_str(self) -> String {
self.stdout
String::from_utf8(self.stdout).unwrap()
}
/// Returns the program's standard output as a vec of bytes
/// consumes self
pub fn stdout_move_bytes(self) -> Vec<u8> {
Vec::from(self.stdout)
self.stdout
}
/// Returns a reference to the program's standard error as a slice of bytes
pub fn stderr(&self) -> &[u8] {
&self.stderr.as_bytes()
&self.stderr
}
/// Returns the program's standard error as a string slice
pub fn stderr_str(&self) -> &str {
&self.stderr
std::str::from_utf8(&self.stderr).unwrap()
}
/// Returns the program's standard error as a string
/// consumes self
pub fn stderr_move_str(self) -> String {
self.stderr
String::from_utf8(self.stderr).unwrap()
}
/// Returns the program's standard error as a vec of bytes
/// consumes self
pub fn stderr_move_bytes(self) -> Vec<u8> {
Vec::from(self.stderr)
self.stderr
}
/// Returns the program's exit code
@ -202,21 +198,21 @@ impl CmdResult {
/// passed in value, trailing whitespace are kept to force strict comparison (#1235)
/// stdout_only is a better choice unless stderr may or will be non-empty
pub fn stdout_is<T: AsRef<str>>(&self, msg: T) -> &CmdResult {
assert_eq!(self.stdout, String::from(msg.as_ref()));
assert_eq!(self.stdout_str(), String::from(msg.as_ref()));
self
}
/// Like `stdout_is` but newlines are normalized to `\n`.
pub fn normalized_newlines_stdout_is<T: AsRef<str>>(&self, msg: T) -> &CmdResult {
let msg = msg.as_ref().replace("\r\n", "\n");
assert_eq!(self.stdout.replace("\r\n", "\n"), msg);
assert_eq!(self.stdout_str().replace("\r\n", "\n"), msg);
self
}
/// asserts that the command resulted in stdout stream output,
/// whose bytes equal those of the passed in slice
pub fn stdout_is_bytes<T: AsRef<[u8]>>(&self, msg: T) -> &CmdResult {
assert_eq!(self.stdout.as_bytes(), msg.as_ref());
assert_eq!(self.stdout, msg.as_ref());
self
}
@ -231,7 +227,7 @@ impl CmdResult {
/// stderr_only is a better choice unless stdout may or will be non-empty
pub fn stderr_is<T: AsRef<str>>(&self, msg: T) -> &CmdResult {
assert_eq!(
self.stderr.trim_end(),
self.stderr_str().trim_end(),
String::from(msg.as_ref()).trim_end()
);
self
@ -240,7 +236,7 @@ impl CmdResult {
/// asserts that the command resulted in stderr stream output,
/// whose bytes equal those of the passed in slice
pub fn stderr_is_bytes<T: AsRef<[u8]>>(&self, msg: T) -> &CmdResult {
assert_eq!(self.stderr.as_bytes(), msg.as_ref());
assert_eq!(self.stderr, msg.as_ref());
self
}
@ -465,7 +461,7 @@ impl AtPath {
.append(true)
.open(self.plus(name))
.unwrap();
f.write(contents.as_bytes())
f.write_all(contents.as_bytes())
.unwrap_or_else(|e| panic!("Couldn't write {}: {}", name, e));
}
@ -778,7 +774,7 @@ impl UCommand {
if self.has_run {
panic!("{}", ALREADY_RUN);
}
self.comm_string.push_str(" ");
self.comm_string.push(' ');
self.comm_string
.push_str(arg.as_ref().to_str().unwrap_or_default());
self.raw.arg(arg.as_ref());
@ -798,7 +794,7 @@ impl UCommand {
.accept_any();
for s in strings {
self.comm_string.push_str(" ");
self.comm_string.push(' ');
self.comm_string.push_str(&s);
}
@ -854,9 +850,9 @@ impl UCommand {
log_info("run", &self.comm_string);
let mut child = self
.raw
.stdin(self.stdin.take().unwrap_or_else(|| Stdio::piped()))
.stdout(self.stdout.take().unwrap_or_else(|| Stdio::piped()))
.stderr(self.stderr.take().unwrap_or_else(|| Stdio::piped()))
.stdin(self.stdin.take().unwrap_or_else(Stdio::piped))
.stdout(self.stdout.take().unwrap_or_else(Stdio::piped))
.stderr(self.stderr.take().unwrap_or_else(Stdio::piped))
.spawn()
.unwrap();
@ -886,8 +882,8 @@ impl UCommand {
tmpd: self.tmpd.clone(),
code: prog.status.code(),
success: prog.status.success(),
stdout: from_utf8(&prog.stdout).unwrap().to_string(),
stderr: from_utf8(&prog.stderr).unwrap().to_string(),
stdout: prog.stdout,
stderr: prog.stderr,
}
}
@ -930,10 +926,7 @@ pub fn read_size(child: &mut Child, size: usize) -> String {
}
pub fn vec_of_size(n: usize) -> Vec<u8> {
let mut result = Vec::new();
for _ in 0..n {
result.push('a' as u8);
}
let result = vec![b'a'; n];
assert_eq!(result.len(), n);
result
}

5
tests/fixtures/sort/blanks.expected vendored Normal file
View file

@ -0,0 +1,5 @@
a
b
x
x
z

View file

@ -0,0 +1,15 @@
a
_
___
b
_
_
x
_
__________
x
_
___
z
_
__

5
tests/fixtures/sort/blanks.txt vendored Normal file
View file

@ -0,0 +1,5 @@
b
a
z
x
x

View file

@ -6,8 +6,15 @@
-12e-5555.5
0b10 // binary not supported
0x10 // hexadecimal not supported, but it should be
55e-20
55e-20.10
5.5.5.5
10E
64e+
99e-
1000EDKLD
10000K78
+100000
@ -15,5 +22,7 @@
100E6
100E6
10e10e10e10
13e+10
45e+10.5
50e10
50e10

View file

@ -22,12 +22,33 @@
^ no match for key
^ no match for key
> -12e-5555.5
_________
______________
0b10 // binary not supported
_
____________________________
0x10 // hexadecimal not supported, but it should be
_
___________________________________________________
55e-20
______
______
55e-20.10
______
_________
5.5.5.5
___
_______
10E
__
___
64e+
__
____
99e-
__
____
1000EDKLD
____
_________
@ -49,6 +70,12 @@ _____
10e10e10e10
_____
___________
13e+10
______
______
45e+10.5
______
________
50e10
_____
_____

View file

@ -4,16 +4,25 @@
+100000
10000K78
0x10 // hexadecimal not supported, but it should be
10E
0b10 // binary not supported
64e+
99e-
45e+10.5
1000EDKLD
13e+10
100E6
50e10
-12e-5555.5
+100000
10e10e10e10
5.5.5.5
55e-20
55e-20.10

View file

@ -0,0 +1,3 @@
cab
abc
bca

View file

@ -0,0 +1,9 @@
>cab
__
____
>abc
__
____
>bca
__
____

3
tests/fixtures/sort/keys_blanks.txt vendored Normal file
View file

@ -0,0 +1,3 @@
abc
cab
bca

View file

@ -0,0 +1,2 @@
1a
1A

View file

@ -0,0 +1,6 @@
1a
_
___
1A
_
__

View file

@ -0,0 +1,2 @@
1a
1A

View file

@ -0,0 +1 @@
a b c

View file

@ -0,0 +1,3 @@
a b c
^ no match for key
_____

View file

@ -0,0 +1 @@
a b c

View file

@ -21,10 +21,10 @@ CARAvan
8.013
45
46.89
4567.
37800
576,446.88800000
576,446.890
4567.
37800
4798908.340000000000
4798908.45
4798908.8909800

View file

@ -67,18 +67,18 @@ __
46.89
_____
_____
576,446.88800000
___
________________
576,446.890
___
___________
4567.
_____
____________________
>>>>37800
_____
_________
576,446.88800000
________________
________________
576,446.890
___________
___________
4798908.340000000000
____________________
____________________

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