mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge branch 'master' into implement-more
This commit is contained in:
Generated
+75
-39
@@ -1,7 +1,5 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "advapi32-sys"
|
||||
version = "0.2.0"
|
||||
@@ -14,11 +12,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr 2.3.4",
|
||||
"memchr 2.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -108,12 +106,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
|
||||
checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr 2.3.4",
|
||||
"memchr 2.4.0",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
@@ -165,13 +163,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.11"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -334,6 +334,16 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coz"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef55b3fe2f5477d59e12bc792e8b3c95a25bd099eadcfae006ecea136de76e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpp"
|
||||
version = "0.5.6"
|
||||
@@ -486,9 +496,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.3"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
|
||||
checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
@@ -499,9 +509,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
|
||||
checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
@@ -552,7 +562,7 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr 2.3.4",
|
||||
"memchr 2.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -634,7 +644,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.2.6",
|
||||
"redox_syscall 0.2.7",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@@ -846,6 +856,15 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "locale"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fdbe492a9c0238da900a1165c42fc5067161ce292678a6fe80921f30fe307fd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
@@ -893,9 +912,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@@ -1170,7 +1189,7 @@ version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.1",
|
||||
"unicode-xid 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1340,9 +1359,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
|
||||
checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -1353,17 +1372,17 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall 0.2.6",
|
||||
"redox_syscall 0.2.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.6"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
|
||||
checksum = "ce5f1ceb7f74abbce32601642fcf8e8508a8a8991e0621c7d750295b9095702b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr 2.3.4",
|
||||
"memchr 2.4.0",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
@@ -1378,9 +1397,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.23"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
@@ -1453,6 +1472,9 @@ name = "serde"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
@@ -1551,6 +1573,9 @@ name = "smallvec"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
@@ -1578,13 +1603,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.70"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883"
|
||||
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"unicode-xid 0.2.1",
|
||||
"unicode-xid 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1638,7 +1663,7 @@ checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall 0.2.6",
|
||||
"redox_syscall 0.2.7",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
@@ -1687,12 +1712,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.42"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_syscall 0.1.57",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@@ -1732,9 +1756,9 @@ checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
@@ -1784,6 +1808,8 @@ dependencies = [
|
||||
name = "uu_base64"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"uu_base32",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
@@ -1900,7 +1926,9 @@ dependencies = [
|
||||
name = "uu_cut"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"clap",
|
||||
"memchr 2.4.0",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
@@ -2002,6 +2030,7 @@ dependencies = [
|
||||
name = "uu_factor"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"coz",
|
||||
"criterion",
|
||||
"num-traits",
|
||||
"paste",
|
||||
@@ -2134,6 +2163,7 @@ dependencies = [
|
||||
name = "uu_kill"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
@@ -2143,6 +2173,7 @@ dependencies = [
|
||||
name = "uu_link"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
@@ -2172,15 +2203,16 @@ name = "uu_ls"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"chrono",
|
||||
"clap",
|
||||
"globset",
|
||||
"lazy_static",
|
||||
"locale",
|
||||
"lscolors",
|
||||
"number_prefix",
|
||||
"once_cell",
|
||||
"term_grid",
|
||||
"termsize",
|
||||
"time",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
@@ -2268,7 +2300,7 @@ dependencies = [
|
||||
"aho-corasick",
|
||||
"clap",
|
||||
"libc",
|
||||
"memchr 2.3.4",
|
||||
"memchr 2.4.0",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"uucore",
|
||||
@@ -2340,6 +2372,7 @@ dependencies = [
|
||||
name = "uu_pinky"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
@@ -2369,7 +2402,7 @@ dependencies = [
|
||||
"aho-corasick",
|
||||
"clap",
|
||||
"libc",
|
||||
"memchr 2.3.4",
|
||||
"memchr 2.4.0",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"uucore",
|
||||
@@ -2484,7 +2517,10 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"rayon",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec 1.6.1",
|
||||
"tempdir",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
||||
@@ -429,6 +429,7 @@ This is an auto-generated table showing which binaries compile for each target-t
|
||||
|windows-msvc|i686|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y|y|y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y| |y|y|y| |y| |y| |y|y|
|
||||
|windows-gnu|x86_64|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y|y|y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y| |y| |y| |y|y|
|
||||
|windows-msvc|x86_64|y|y|y|y|y| | | | |y|y|y|y|y|y|y|y|y| |y|y|y| |y|y|y|y| |y|y|y|y| | |y| |y|y|y|y|y| | |y|y|y| |y| |y|y|y|y| | |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| | |y|y|y|y|y|y| |y|y|y|y|y| |y|y|y| |y| |y| |y|y|
|
||||
|apple MacOS|aarch64|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
||||
|apple MacOS|x86_64|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
||||
|freebsd|x86_64|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|
|
||||
|netbsd|x86_64|y|y|y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y|y|y|y|y|y|y| |y|y| |y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y| |y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y|y| |y|y|y|y|y|y| |y|y|y| | |y| |y|y|
|
||||
|
||||
@@ -26,6 +26,7 @@ TARGETS = [
|
||||
"x86_64-pc-windows-gnu",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# Apple
|
||||
"aarch64-apple-darwin",
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-ios",
|
||||
"x86_64-apple-ios",
|
||||
@@ -231,4 +232,4 @@ if __name__ == "__main__":
|
||||
|
||||
prev_table, _, _ = load_csv(CACHE_PATH)
|
||||
new_table = merge_tables(prev_table, table)
|
||||
save_csv(CACHE_PATH, new_table)
|
||||
save_csv(CACHE_PATH, new_table)
|
||||
|
||||
+31
-121
@@ -7,19 +7,14 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use std::io::{stdin, Read};
|
||||
|
||||
use uucore::encoding::Format;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, BufReader};
|
||||
use std::path::Path;
|
||||
pub mod base_common;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
mod base_common;
|
||||
|
||||
static SUMMARY: &str = "Base32 encode or decode FILE, or standard input, to standard output.";
|
||||
static LONG_HELP: &str = "
|
||||
static ABOUT: &str = "
|
||||
With no FILE, or when FILE is -, read standard input.
|
||||
|
||||
The data are encoded as described for the base32 alphabet in RFC
|
||||
@@ -30,126 +25,41 @@ static LONG_HELP: &str = "
|
||||
";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
static BASE_CMD_PARSE_ERROR: i32 = 1;
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} [OPTION]... [FILE]", executable!())
|
||||
}
|
||||
|
||||
pub mod options {
|
||||
pub static DECODE: &str = "decode";
|
||||
pub static WRAP: &str = "wrap";
|
||||
pub static IGNORE_GARBAGE: &str = "ignore-garbage";
|
||||
pub static FILE: &str = "file";
|
||||
}
|
||||
|
||||
struct Config {
|
||||
decode: bool,
|
||||
ignore_garbage: bool,
|
||||
wrap_cols: Option<usize>,
|
||||
to_read: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn from(options: clap::ArgMatches) -> Config {
|
||||
let file: Option<String> = match options.values_of(options::FILE) {
|
||||
Some(mut values) => {
|
||||
let name = values.next().unwrap();
|
||||
if values.len() != 0 {
|
||||
crash!(3, "extra operand ‘{}’", name);
|
||||
}
|
||||
|
||||
if name == "-" {
|
||||
None
|
||||
} else {
|
||||
if !Path::exists(Path::new(name)) {
|
||||
crash!(2, "{}: No such file or directory", name);
|
||||
}
|
||||
Some(name.to_owned())
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let cols = match options.value_of(options::WRAP) {
|
||||
Some(num) => match num.parse::<usize>() {
|
||||
Ok(n) => Some(n),
|
||||
Err(e) => {
|
||||
crash!(1, "invalid wrap size: ‘{}’: {}", num, e);
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
Config {
|
||||
decode: options.is_present(options::DECODE),
|
||||
ignore_garbage: options.is_present(options::IGNORE_GARBAGE),
|
||||
wrap_cols: cols,
|
||||
to_read: file,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let format = Format::Base32;
|
||||
let usage = get_usage();
|
||||
let app = App::new(executable!())
|
||||
.version(VERSION)
|
||||
.about(SUMMARY)
|
||||
.usage(&usage[..])
|
||||
.about(LONG_HELP)
|
||||
// Format arguments.
|
||||
.arg(
|
||||
Arg::with_name(options::DECODE)
|
||||
.short("d")
|
||||
.long(options::DECODE)
|
||||
.help("decode data"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::IGNORE_GARBAGE)
|
||||
.short("i")
|
||||
.long(options::IGNORE_GARBAGE)
|
||||
.help("when decoding, ignore non-alphabetic characters"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::WRAP)
|
||||
.short("w")
|
||||
.long(options::WRAP)
|
||||
.takes_value(true)
|
||||
.help(
|
||||
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
|
||||
),
|
||||
)
|
||||
// "multiple" arguments are used to check whether there is more than one
|
||||
// file passed in.
|
||||
.arg(Arg::with_name(options::FILE).index(1).multiple(true));
|
||||
let name = executable!();
|
||||
|
||||
let arg_list = args
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
let config: Config = Config::from(app.get_matches_from(arg_list));
|
||||
match config.to_read {
|
||||
// Read from file.
|
||||
Some(name) => {
|
||||
let file_buf = safe_unwrap!(File::open(Path::new(&name)));
|
||||
let mut input = BufReader::new(file_buf);
|
||||
base_common::handle_input(
|
||||
&mut input,
|
||||
format,
|
||||
config.wrap_cols,
|
||||
config.ignore_garbage,
|
||||
config.decode,
|
||||
);
|
||||
let config_result: Result<base_common::Config, String> =
|
||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
||||
|
||||
if config_result.is_err() {
|
||||
match config_result {
|
||||
Ok(_) => panic!(),
|
||||
Err(s) => crash!(BASE_CMD_PARSE_ERROR, "{}", s),
|
||||
}
|
||||
// stdin
|
||||
None => {
|
||||
base_common::handle_input(
|
||||
&mut stdin().lock(),
|
||||
format,
|
||||
config.wrap_cols,
|
||||
config.ignore_garbage,
|
||||
config.decode,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Create a reference to stdin so we can return a locked stdin from
|
||||
// parse_base_cmd_args
|
||||
let stdin_raw = stdin();
|
||||
let config = config_result.unwrap();
|
||||
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw);
|
||||
|
||||
base_common::handle_input(
|
||||
&mut input,
|
||||
format,
|
||||
config.wrap_cols,
|
||||
config.ignore_garbage,
|
||||
config.decode,
|
||||
name,
|
||||
);
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
@@ -10,6 +10,122 @@
|
||||
use std::io::{stdout, Read, Write};
|
||||
|
||||
use uucore::encoding::{wrap_print, Data, Format};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Stdin};
|
||||
use std::path::Path;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
// Config.
|
||||
pub struct Config {
|
||||
pub decode: bool,
|
||||
pub ignore_garbage: bool,
|
||||
pub wrap_cols: Option<usize>,
|
||||
pub to_read: Option<String>,
|
||||
}
|
||||
|
||||
pub mod options {
|
||||
pub static DECODE: &str = "decode";
|
||||
pub static WRAP: &str = "wrap";
|
||||
pub static IGNORE_GARBAGE: &str = "ignore-garbage";
|
||||
pub static FILE: &str = "file";
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn from(options: clap::ArgMatches) -> Result<Config, String> {
|
||||
let file: Option<String> = match options.values_of(options::FILE) {
|
||||
Some(mut values) => {
|
||||
let name = values.next().unwrap();
|
||||
if values.len() != 0 {
|
||||
return Err(format!("extra operand ‘{}’", name));
|
||||
}
|
||||
|
||||
if name == "-" {
|
||||
None
|
||||
} else {
|
||||
if !Path::exists(Path::new(name)) {
|
||||
return Err(format!("{}: No such file or directory", name));
|
||||
}
|
||||
Some(name.to_owned())
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let cols = match options.value_of(options::WRAP) {
|
||||
Some(num) => match num.parse::<usize>() {
|
||||
Ok(n) => Some(n),
|
||||
Err(e) => {
|
||||
return Err(format!("Invalid wrap size: ‘{}’: {}", num, e));
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(Config {
|
||||
decode: options.is_present(options::DECODE),
|
||||
ignore_garbage: options.is_present(options::IGNORE_GARBAGE),
|
||||
wrap_cols: cols,
|
||||
to_read: file,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_base_cmd_args(
|
||||
args: impl uucore::Args,
|
||||
name: &str,
|
||||
version: &str,
|
||||
about: &str,
|
||||
usage: &str,
|
||||
) -> Result<Config, String> {
|
||||
let app = App::new(name)
|
||||
.version(version)
|
||||
.about(about)
|
||||
.usage(usage)
|
||||
// Format arguments.
|
||||
.arg(
|
||||
Arg::with_name(options::DECODE)
|
||||
.short("d")
|
||||
.long(options::DECODE)
|
||||
.help("decode data"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::IGNORE_GARBAGE)
|
||||
.short("i")
|
||||
.long(options::IGNORE_GARBAGE)
|
||||
.help("when decoding, ignore non-alphabetic characters"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::WRAP)
|
||||
.short("w")
|
||||
.long(options::WRAP)
|
||||
.takes_value(true)
|
||||
.help(
|
||||
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
|
||||
),
|
||||
)
|
||||
// "multiple" arguments are used to check whether there is more than one
|
||||
// file passed in.
|
||||
.arg(Arg::with_name(options::FILE).index(1).multiple(true));
|
||||
let arg_list = args
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
Config::from(app.get_matches_from(arg_list))
|
||||
}
|
||||
|
||||
pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> Box<dyn Read + 'a> {
|
||||
match &config.to_read {
|
||||
Some(name) => {
|
||||
let file_buf = safe_unwrap!(File::open(Path::new(name)));
|
||||
Box::new(BufReader::new(file_buf)) // as Box<dyn Read>
|
||||
}
|
||||
None => {
|
||||
Box::new(stdin_ref.lock()) // as Box<dyn Read>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input<R: Read>(
|
||||
input: &mut R,
|
||||
@@ -17,6 +133,7 @@ pub fn handle_input<R: Read>(
|
||||
line_wrap: Option<usize>,
|
||||
ignore_garbage: bool,
|
||||
decode: bool,
|
||||
name: &str,
|
||||
) {
|
||||
let mut data = Data::new(input, format).ignore_garbage(ignore_garbage);
|
||||
if let Some(wrap) = line_wrap {
|
||||
@@ -31,10 +148,14 @@ pub fn handle_input<R: Read>(
|
||||
Ok(s) => {
|
||||
if stdout().write_all(&s).is_err() {
|
||||
// on windows console, writing invalid utf8 returns an error
|
||||
crash!(1, "Cannot write non-utf8 data");
|
||||
eprintln!("{}: error: Cannot write non-utf8 data", name);
|
||||
exit!(1)
|
||||
}
|
||||
}
|
||||
Err(_) => crash!(1, "invalid input"),
|
||||
Err(_) => {
|
||||
eprintln!("{}: error: invalid input", name);
|
||||
exit!(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ edition = "2018"
|
||||
path = "src/base64.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features = ["encoding"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
uu_base32 = { version=">=0.0.6", package="uu_base32", path="../base32"}
|
||||
|
||||
[[bin]]
|
||||
name = "base64"
|
||||
|
||||
+41
-13
@@ -8,14 +8,14 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use uu_base32::base_common;
|
||||
|
||||
use uucore::encoding::Format;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
mod base_common;
|
||||
use std::io::{stdin, Read};
|
||||
|
||||
static SYNTAX: &str = "[OPTION]... [FILE]";
|
||||
static SUMMARY: &str = "Base64 encode or decode FILE, or standard input, to standard output.";
|
||||
static LONG_HELP: &str = "
|
||||
static ABOUT: &str = "
|
||||
With no FILE, or when FILE is -, read standard input.
|
||||
|
||||
The data are encoded as described for the base64 alphabet in RFC
|
||||
@@ -24,14 +24,42 @@ static LONG_HELP: &str = "
|
||||
to attempt to recover from any other non-alphabet bytes in the
|
||||
encoded stream.
|
||||
";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
static BASE_CMD_PARSE_ERROR: i32 = 1;
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} [OPTION]... [FILE]", executable!())
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
base_common::execute(
|
||||
args.collect_str(InvalidEncodingHandling::Ignore)
|
||||
.accept_any(),
|
||||
SYNTAX,
|
||||
SUMMARY,
|
||||
LONG_HELP,
|
||||
Format::Base64,
|
||||
)
|
||||
let format = Format::Base64;
|
||||
let usage = get_usage();
|
||||
let name = executable!();
|
||||
let config_result: Result<base_common::Config, String> =
|
||||
base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
|
||||
|
||||
if config_result.is_err() {
|
||||
match config_result {
|
||||
Ok(_) => panic!(),
|
||||
Err(s) => crash!(BASE_CMD_PARSE_ERROR, "{}", s),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a reference to stdin so we can return a locked stdin from
|
||||
// parse_base_cmd_args
|
||||
let stdin_raw = stdin();
|
||||
let config = config_result.unwrap();
|
||||
let mut input: Box<dyn Read> = base_common::get_input(&config, &stdin_raw);
|
||||
|
||||
base_common::handle_input(
|
||||
&mut input,
|
||||
format,
|
||||
config.wrap_cols,
|
||||
config.ignore_garbage,
|
||||
config.decode,
|
||||
name,
|
||||
);
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// (c) Jordy Dickinson <jordy.dickinson@gmail.com>
|
||||
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
||||
// (c) Alex Lyon <arcterus@mail.com>
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE file
|
||||
// that was distributed with this source code.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, stdout, BufReader, Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use uucore::encoding::{wrap_print, Data, Format};
|
||||
|
||||
pub fn execute(
|
||||
args: Vec<String>,
|
||||
syntax: &str,
|
||||
summary: &str,
|
||||
long_help: &str,
|
||||
format: Format,
|
||||
) -> i32 {
|
||||
let matches = app!(syntax, summary, long_help)
|
||||
.optflag("d", "decode", "decode data")
|
||||
.optflag(
|
||||
"i",
|
||||
"ignore-garbage",
|
||||
"when decoding, ignore non-alphabetic characters",
|
||||
)
|
||||
.optopt(
|
||||
"w",
|
||||
"wrap",
|
||||
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
|
||||
"COLS",
|
||||
)
|
||||
.parse(args);
|
||||
|
||||
let line_wrap = matches.opt_str("wrap").map(|s| match s.parse() {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
crash!(1, "invalid wrap size: ‘{}’: {}", s, e);
|
||||
}
|
||||
});
|
||||
let ignore_garbage = matches.opt_present("ignore-garbage");
|
||||
let decode = matches.opt_present("decode");
|
||||
|
||||
if matches.free.len() > 1 {
|
||||
show_usage_error!("extra operand ‘{}’", matches.free[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if matches.free.is_empty() || &matches.free[0][..] == "-" {
|
||||
let stdin_raw = stdin();
|
||||
handle_input(
|
||||
&mut stdin_raw.lock(),
|
||||
format,
|
||||
line_wrap,
|
||||
ignore_garbage,
|
||||
decode,
|
||||
);
|
||||
} else {
|
||||
let path = Path::new(matches.free[0].as_str());
|
||||
let file_buf = safe_unwrap!(File::open(&path));
|
||||
let mut input = BufReader::new(file_buf);
|
||||
handle_input(&mut input, format, line_wrap, ignore_garbage, decode);
|
||||
};
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn handle_input<R: Read>(
|
||||
input: &mut R,
|
||||
format: Format,
|
||||
line_wrap: Option<usize>,
|
||||
ignore_garbage: bool,
|
||||
decode: bool,
|
||||
) {
|
||||
let mut data = Data::new(input, format).ignore_garbage(ignore_garbage);
|
||||
if let Some(wrap) = line_wrap {
|
||||
data = data.line_wrap(wrap);
|
||||
}
|
||||
|
||||
if !decode {
|
||||
let encoded = data.encode();
|
||||
wrap_print(&data, encoded);
|
||||
} else {
|
||||
match data.decode() {
|
||||
Ok(s) => {
|
||||
if stdout().write_all(&s).is_err() {
|
||||
// on windows console, writing invalid utf8 returns an error
|
||||
crash!(1, "Cannot write non-utf8 data");
|
||||
}
|
||||
}
|
||||
Err(_) => crash!(1, "invalid input"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ edition = "2018"
|
||||
path = "src/basename.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.2"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
||||
@@ -10,83 +10,106 @@
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::{is_separator, PathBuf};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static NAME: &str = "basename";
|
||||
static SYNTAX: &str = "NAME [SUFFIX]";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static SUMMARY: &str = "Print NAME with any leading directory components removed
|
||||
If specified, also remove a trailing SUFFIX";
|
||||
static LONG_HELP: &str = "";
|
||||
If specified, also remove a trailing SUFFIX";
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!(
|
||||
"{0} NAME [SUFFIX]
|
||||
{0} OPTION... NAME...",
|
||||
executable!()
|
||||
)
|
||||
}
|
||||
|
||||
pub mod options {
|
||||
pub static MULTIPLE: &str = "multiple";
|
||||
pub static NAME: &str = "name";
|
||||
pub static SUFFIX: &str = "suffix";
|
||||
pub static ZERO: &str = "zero";
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let args = args
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
let usage = get_usage();
|
||||
//
|
||||
// Argument parsing
|
||||
//
|
||||
let matches = app!(SYNTAX, SUMMARY, LONG_HELP)
|
||||
.optflag(
|
||||
"a",
|
||||
"multiple",
|
||||
"Support more than one argument. Treat every argument as a name.",
|
||||
let matches = App::new(executable!())
|
||||
.version(VERSION)
|
||||
.about(SUMMARY)
|
||||
.usage(&usage[..])
|
||||
.arg(
|
||||
Arg::with_name(options::MULTIPLE)
|
||||
.short("a")
|
||||
.long(options::MULTIPLE)
|
||||
.help("support multiple arguments and treat each as a NAME"),
|
||||
)
|
||||
.optopt(
|
||||
"s",
|
||||
"suffix",
|
||||
"Remove a trailing suffix. This option implies the -a option.",
|
||||
"SUFFIX",
|
||||
.arg(Arg::with_name(options::NAME).multiple(true).hidden(true))
|
||||
.arg(
|
||||
Arg::with_name(options::SUFFIX)
|
||||
.short("s")
|
||||
.long(options::SUFFIX)
|
||||
.value_name("SUFFIX")
|
||||
.help("remove a trailing SUFFIX; implies -a"),
|
||||
)
|
||||
.optflag(
|
||||
"z",
|
||||
"zero",
|
||||
"Output a zero byte (ASCII NUL) at the end of each line, rather than a newline.",
|
||||
.arg(
|
||||
Arg::with_name(options::ZERO)
|
||||
.short("z")
|
||||
.long(options::ZERO)
|
||||
.help("end each output line with NUL, not newline"),
|
||||
)
|
||||
.parse(args);
|
||||
.get_matches_from(args);
|
||||
|
||||
// too few arguments
|
||||
if matches.free.is_empty() {
|
||||
if !matches.is_present(options::NAME) {
|
||||
crash!(
|
||||
1,
|
||||
"{0}: {1}\nTry '{0} --help' for more information.",
|
||||
NAME,
|
||||
"{1}\nTry '{0} --help' for more information.",
|
||||
executable!(),
|
||||
"missing operand"
|
||||
);
|
||||
}
|
||||
let opt_s = matches.opt_present("s");
|
||||
let opt_a = matches.opt_present("a");
|
||||
let opt_z = matches.opt_present("z");
|
||||
let multiple_paths = opt_s || opt_a;
|
||||
|
||||
let opt_suffix = matches.is_present(options::SUFFIX);
|
||||
let opt_multiple = matches.is_present(options::MULTIPLE);
|
||||
let opt_zero = matches.is_present(options::ZERO);
|
||||
let multiple_paths = opt_suffix || opt_multiple;
|
||||
// too many arguments
|
||||
if !multiple_paths && matches.free.len() > 2 {
|
||||
if !multiple_paths && matches.occurrences_of(options::NAME) > 2 {
|
||||
crash!(
|
||||
1,
|
||||
"{0}: extra operand '{1}'\nTry '{0} --help' for more information.",
|
||||
NAME,
|
||||
matches.free[2]
|
||||
"extra operand '{1}'\nTry '{0} --help' for more information.",
|
||||
executable!(),
|
||||
matches.values_of(options::NAME).unwrap().nth(2).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
let suffix = if opt_s {
|
||||
matches.opt_str("s").unwrap()
|
||||
} else if !opt_a && matches.free.len() > 1 {
|
||||
matches.free[1].clone()
|
||||
let suffix = if opt_suffix {
|
||||
matches.value_of(options::SUFFIX).unwrap()
|
||||
} else if !opt_multiple && matches.occurrences_of(options::NAME) > 1 {
|
||||
matches.values_of(options::NAME).unwrap().nth(1).unwrap()
|
||||
} else {
|
||||
"".to_owned()
|
||||
""
|
||||
};
|
||||
|
||||
//
|
||||
// Main Program Processing
|
||||
//
|
||||
|
||||
let paths = if multiple_paths {
|
||||
&matches.free[..]
|
||||
let paths: Vec<_> = if multiple_paths {
|
||||
matches.values_of(options::NAME).unwrap().collect()
|
||||
} else {
|
||||
&matches.free[0..1]
|
||||
matches.values_of(options::NAME).unwrap().take(1).collect()
|
||||
};
|
||||
|
||||
let line_ending = if opt_z { "\0" } else { "\n" };
|
||||
let line_ending = if opt_zero { "\0" } else { "\n" };
|
||||
for path in paths {
|
||||
print!("{}{}", basename(&path, &suffix), line_ending);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ fn copy_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<
|
||||
let mut buf = [0; BUF_SIZE];
|
||||
loop {
|
||||
let read = unistd::read(read_fd, &mut buf[..left])?;
|
||||
let written = unistd::write(write_fd, &mut buf[..read])?;
|
||||
let written = unistd::write(write_fd, &buf[..read])?;
|
||||
left -= written;
|
||||
if left == 0 {
|
||||
break;
|
||||
|
||||
@@ -551,6 +551,10 @@ impl FromStr for Attribute {
|
||||
fn add_all_attributes() -> Vec<Attribute> {
|
||||
use Attribute::*;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let attr = vec![Ownership, Timestamps, Context, Xattr, Links];
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let mut attr = vec![Ownership, Timestamps, Context, Xattr, Links];
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
## Benchmarking cut
|
||||
|
||||
### Performance profile
|
||||
|
||||
In normal use cases a significant amount of the total execution time of `cut`
|
||||
is spent performing I/O. When invoked with the `-f` option (cut fields) some
|
||||
CPU time is spent on detecting fields (in `Searcher::next`). Other than that
|
||||
some small amount of CPU time is spent on breaking the input stream into lines.
|
||||
|
||||
|
||||
### How to
|
||||
|
||||
When fixing bugs or adding features you might want to compare
|
||||
performance before and after your code changes.
|
||||
|
||||
- `hyperfine` can be used to accurately measure and compare the total
|
||||
execution time of one or more commands.
|
||||
|
||||
```
|
||||
$ cargo build --release --package uu_cut
|
||||
|
||||
$ hyperfine -w3 "./target/release/cut -f2-4,8 -d' ' input.txt" "cut -f2-4,8 -d' ' input.txt"
|
||||
```
|
||||
You can put those two commands in a shell script to be sure that you don't
|
||||
forget to build after making any changes.
|
||||
|
||||
When optimizing or fixing performance regressions seeing the number of times a
|
||||
function is called, and the amount of time it takes can be useful.
|
||||
|
||||
- `cargo flamegraph` generates flame graphs from function level metrics it records using `perf` or `dtrace`
|
||||
|
||||
```
|
||||
$ cargo flamegraph --bin cut --package uu_cut -- -f1,3-4 input.txt > /dev/null
|
||||
```
|
||||
|
||||
|
||||
### What to benchmark
|
||||
|
||||
There are four different performance paths in `cut` to benchmark.
|
||||
|
||||
- Byte ranges `-c`/`--characters` or `-b`/`--bytes` e.g. `cut -c 2,4,6-`
|
||||
- Byte ranges with output delimiters e.g. `cut -c 4- --output-delimiter=/`
|
||||
- Fields e.g. `cut -f -4`
|
||||
- Fields with output delimiters e.g. `cut -f 7-10 --output-delimiter=:`
|
||||
|
||||
Choose a test input file with large number of lines so that program startup time does not significantly affect the benchmark.
|
||||
@@ -18,6 +18,8 @@ path = "src/cut.rs"
|
||||
clap = "2.33"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
memchr = "2"
|
||||
bstr = "0.2"
|
||||
|
||||
[[bin]]
|
||||
name = "cut"
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// (c) Rolf Morel <rolfmorel@gmail.com>
|
||||
// (c) kwantam <kwantam@gmail.com>
|
||||
// * substantial rewrite to use the `std::io::BufReader` trait
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) SRes Newl
|
||||
|
||||
use std::io::Result as IoResult;
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub mod Bytes {
|
||||
use std::io::Write;
|
||||
|
||||
pub trait Select {
|
||||
fn select<W: Write>(&mut self, bytes: usize, out: Option<&mut W>) -> Selected;
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum Selected {
|
||||
NewlineFound,
|
||||
Complete(usize),
|
||||
Partial(usize),
|
||||
EndOfFile,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ByteReader<R>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
inner: BufReader<R>,
|
||||
newline_char: u8,
|
||||
}
|
||||
|
||||
impl<R: Read> ByteReader<R> {
|
||||
pub fn new(read: R, newline_char: u8) -> ByteReader<R> {
|
||||
ByteReader {
|
||||
inner: BufReader::with_capacity(4096, read),
|
||||
newline_char,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Read for ByteReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> BufRead for ByteReader<R> {
|
||||
fn fill_buf(&mut self) -> IoResult<&[u8]> {
|
||||
self.inner.fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> ByteReader<R> {
|
||||
pub fn consume_line(&mut self) -> usize {
|
||||
let mut bytes_consumed = 0;
|
||||
let mut consume_val;
|
||||
let newline_char = self.newline_char;
|
||||
|
||||
loop {
|
||||
{
|
||||
// need filled_buf to go out of scope
|
||||
let filled_buf = match self.fill_buf() {
|
||||
Ok(b) => {
|
||||
if b.is_empty() {
|
||||
return bytes_consumed;
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
Err(e) => crash!(1, "read error: {}", e),
|
||||
};
|
||||
|
||||
if let Some(idx) = filled_buf.iter().position(|byte| *byte == newline_char) {
|
||||
consume_val = idx + 1;
|
||||
bytes_consumed += consume_val;
|
||||
break;
|
||||
}
|
||||
|
||||
consume_val = filled_buf.len();
|
||||
}
|
||||
|
||||
bytes_consumed += consume_val;
|
||||
self.consume(consume_val);
|
||||
}
|
||||
|
||||
self.consume(consume_val);
|
||||
bytes_consumed
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> self::Bytes::Select for ByteReader<R> {
|
||||
fn select<W: Write>(&mut self, bytes: usize, out: Option<&mut W>) -> Bytes::Selected {
|
||||
enum SRes {
|
||||
Comp,
|
||||
Part,
|
||||
Newl,
|
||||
}
|
||||
|
||||
use self::Bytes::Selected::*;
|
||||
|
||||
let newline_char = self.newline_char;
|
||||
let (res, consume_val) = {
|
||||
let buffer = match self.fill_buf() {
|
||||
Err(e) => crash!(1, "read error: {}", e),
|
||||
Ok(b) => b,
|
||||
};
|
||||
|
||||
let (res, consume_val) = match buffer.len() {
|
||||
0 => return EndOfFile,
|
||||
buf_used if bytes < buf_used => {
|
||||
// because the output delimiter should only be placed between
|
||||
// segments check if the byte after bytes is a newline
|
||||
let buf_slice = &buffer[0..=bytes];
|
||||
|
||||
match buf_slice.iter().position(|byte| *byte == newline_char) {
|
||||
Some(idx) => (SRes::Newl, idx + 1),
|
||||
None => (SRes::Comp, bytes),
|
||||
}
|
||||
}
|
||||
_ => match buffer.iter().position(|byte| *byte == newline_char) {
|
||||
Some(idx) => (SRes::Newl, idx + 1),
|
||||
None => (SRes::Part, buffer.len()),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(out) = out {
|
||||
crash_if_err!(1, out.write_all(&buffer[0..consume_val]));
|
||||
}
|
||||
(res, consume_val)
|
||||
};
|
||||
|
||||
self.consume(consume_val);
|
||||
match res {
|
||||
SRes::Comp => Complete(consume_val),
|
||||
SRes::Part => Partial(consume_val),
|
||||
SRes::Newl => NewlineFound,
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
-130
@@ -10,16 +10,17 @@
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use bstr::io::BufReadExt;
|
||||
use clap::{App, Arg};
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, stdout, BufRead, BufReader, Read, Stdout, Write};
|
||||
use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use self::searcher::Searcher;
|
||||
use uucore::fs::is_stdout_interactive;
|
||||
use uucore::ranges::Range;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
mod buffer;
|
||||
mod searcher;
|
||||
|
||||
static NAME: &str = "cut";
|
||||
@@ -126,6 +127,14 @@ enum Mode {
|
||||
Fields(Vec<Range>, FieldOptions),
|
||||
}
|
||||
|
||||
fn stdout_writer() -> Box<dyn Write> {
|
||||
if is_stdout_interactive() {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(BufWriter::new(stdout())) as Box<dyn Write>
|
||||
}
|
||||
}
|
||||
|
||||
fn list_to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
|
||||
if complement {
|
||||
Range::from_list(list).map(|r| uucore::ranges::complement(&r))
|
||||
@@ -135,72 +144,35 @@ fn list_to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
|
||||
}
|
||||
|
||||
fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
|
||||
use self::buffer::Bytes::Select;
|
||||
use self::buffer::Bytes::Selected::*;
|
||||
|
||||
let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
|
||||
let mut buf_read = buffer::ByteReader::new(reader, newline_char);
|
||||
let mut out = stdout();
|
||||
let buf_in = BufReader::new(reader);
|
||||
let mut out = stdout_writer();
|
||||
let delim = opts
|
||||
.out_delim
|
||||
.as_ref()
|
||||
.map_or("", String::as_str)
|
||||
.as_bytes();
|
||||
|
||||
'newline: loop {
|
||||
let mut cur_pos = 1;
|
||||
let res = buf_in.for_byte_record(newline_char, |line| {
|
||||
let mut print_delim = false;
|
||||
|
||||
for &Range { low, high } in ranges.iter() {
|
||||
// skip up to low
|
||||
let orig_pos = cur_pos;
|
||||
loop {
|
||||
match buf_read.select(low - cur_pos, None::<&mut Stdout>) {
|
||||
NewlineFound => {
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
continue 'newline;
|
||||
}
|
||||
Complete(len) => {
|
||||
cur_pos += len;
|
||||
break;
|
||||
}
|
||||
Partial(len) => cur_pos += len,
|
||||
EndOfFile => {
|
||||
if orig_pos != cur_pos {
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
}
|
||||
|
||||
break 'newline;
|
||||
}
|
||||
}
|
||||
for &Range { low, high } in ranges {
|
||||
if low > line.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(ref delim) = opts.out_delim {
|
||||
if print_delim {
|
||||
crash_if_err!(1, out.write_all(delim.as_bytes()));
|
||||
}
|
||||
if print_delim {
|
||||
out.write_all(delim)?;
|
||||
} else if opts.out_delim.is_some() {
|
||||
print_delim = true;
|
||||
}
|
||||
|
||||
// write out from low to high
|
||||
loop {
|
||||
match buf_read.select(high - cur_pos + 1, Some(&mut out)) {
|
||||
NewlineFound => continue 'newline,
|
||||
Partial(len) => cur_pos += len,
|
||||
Complete(_) => {
|
||||
cur_pos = high + 1;
|
||||
break;
|
||||
}
|
||||
EndOfFile => {
|
||||
if cur_pos != low || low == high {
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
}
|
||||
|
||||
break 'newline;
|
||||
}
|
||||
}
|
||||
}
|
||||
// change `low` from 1-indexed value to 0-index value
|
||||
let low = low - 1;
|
||||
let high = high.min(line.len());
|
||||
out.write_all(&line[low..high])?;
|
||||
}
|
||||
|
||||
buf_read.consume_line();
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
}
|
||||
|
||||
out.write_all(&[newline_char])?;
|
||||
Ok(true)
|
||||
});
|
||||
crash_if_err!(1, res);
|
||||
0
|
||||
}
|
||||
|
||||
@@ -213,23 +185,11 @@ fn cut_fields_delimiter<R: Read>(
|
||||
newline_char: u8,
|
||||
out_delim: &str,
|
||||
) -> i32 {
|
||||
let mut buf_in = BufReader::new(reader);
|
||||
let mut out = stdout();
|
||||
let mut buffer = Vec::new();
|
||||
let buf_in = BufReader::new(reader);
|
||||
let mut out = stdout_writer();
|
||||
let input_delim_len = delim.len();
|
||||
|
||||
'newline: loop {
|
||||
buffer.clear();
|
||||
match buf_in.read_until(newline_char, &mut buffer) {
|
||||
Ok(n) if n == 0 => break,
|
||||
Err(e) => {
|
||||
if buffer.is_empty() {
|
||||
crash!(1, "read error: {}", e);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let line = &buffer[..];
|
||||
let result = buf_in.for_byte_record_with_terminator(newline_char, |line| {
|
||||
let mut fields_pos = 1;
|
||||
let mut low_idx = 0;
|
||||
let mut delim_search = Searcher::new(line, delim.as_bytes()).peekable();
|
||||
@@ -237,46 +197,46 @@ fn cut_fields_delimiter<R: Read>(
|
||||
|
||||
if delim_search.peek().is_none() {
|
||||
if !only_delimited {
|
||||
crash_if_err!(1, out.write_all(line));
|
||||
out.write_all(line)?;
|
||||
if line[line.len() - 1] != newline_char {
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
out.write_all(&[newline_char])?;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
for &Range { low, high } in ranges.iter() {
|
||||
for &Range { low, high } in ranges {
|
||||
if low - fields_pos > 0 {
|
||||
low_idx = match delim_search.nth(low - fields_pos - 1) {
|
||||
Some((_, beyond_delim)) => beyond_delim,
|
||||
Some(index) => index + input_delim_len,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
|
||||
for _ in 0..=high - low {
|
||||
if print_delim {
|
||||
crash_if_err!(1, out.write_all(out_delim.as_bytes()));
|
||||
out.write_all(out_delim.as_bytes())?;
|
||||
} else {
|
||||
print_delim = true;
|
||||
}
|
||||
|
||||
match delim_search.next() {
|
||||
Some((high_idx, next_low_idx)) => {
|
||||
Some(high_idx) => {
|
||||
let segment = &line[low_idx..high_idx];
|
||||
|
||||
crash_if_err!(1, out.write_all(segment));
|
||||
out.write_all(segment)?;
|
||||
|
||||
print_delim = true;
|
||||
|
||||
low_idx = next_low_idx;
|
||||
low_idx = high_idx + input_delim_len;
|
||||
fields_pos = high + 1;
|
||||
}
|
||||
None => {
|
||||
let segment = &line[low_idx..];
|
||||
|
||||
crash_if_err!(1, out.write_all(segment));
|
||||
out.write_all(segment)?;
|
||||
|
||||
if line[line.len() - 1] == newline_char {
|
||||
continue 'newline;
|
||||
return Ok(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -284,9 +244,10 @@ fn cut_fields_delimiter<R: Read>(
|
||||
}
|
||||
}
|
||||
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
}
|
||||
|
||||
out.write_all(&[newline_char])?;
|
||||
Ok(true)
|
||||
});
|
||||
crash_if_err!(1, result);
|
||||
0
|
||||
}
|
||||
|
||||
@@ -304,23 +265,11 @@ fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> i32
|
||||
);
|
||||
}
|
||||
|
||||
let mut buf_in = BufReader::new(reader);
|
||||
let mut out = stdout();
|
||||
let mut buffer = Vec::new();
|
||||
let buf_in = BufReader::new(reader);
|
||||
let mut out = stdout_writer();
|
||||
let delim_len = opts.delimiter.len();
|
||||
|
||||
'newline: loop {
|
||||
buffer.clear();
|
||||
match buf_in.read_until(newline_char, &mut buffer) {
|
||||
Ok(n) if n == 0 => break,
|
||||
Err(e) => {
|
||||
if buffer.is_empty() {
|
||||
crash!(1, "read error: {}", e);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let line = &buffer[..];
|
||||
let result = buf_in.for_byte_record_with_terminator(newline_char, |line| {
|
||||
let mut fields_pos = 1;
|
||||
let mut low_idx = 0;
|
||||
let mut delim_search = Searcher::new(line, opts.delimiter.as_bytes()).peekable();
|
||||
@@ -328,53 +277,54 @@ fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> i32
|
||||
|
||||
if delim_search.peek().is_none() {
|
||||
if !opts.only_delimited {
|
||||
crash_if_err!(1, out.write_all(line));
|
||||
out.write_all(line)?;
|
||||
if line[line.len() - 1] != newline_char {
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
out.write_all(&[newline_char])?;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
for &Range { low, high } in ranges.iter() {
|
||||
for &Range { low, high } in ranges {
|
||||
if low - fields_pos > 0 {
|
||||
low_idx = match delim_search.nth(low - fields_pos - 1) {
|
||||
Some((_, beyond_delim)) => beyond_delim,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
|
||||
if print_delim && low_idx >= opts.delimiter.as_bytes().len() {
|
||||
low_idx -= opts.delimiter.as_bytes().len();
|
||||
if let Some(delim_pos) = delim_search.nth(low - fields_pos - 1) {
|
||||
low_idx = if print_delim {
|
||||
delim_pos
|
||||
} else {
|
||||
delim_pos + delim_len
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match delim_search.nth(high - low) {
|
||||
Some((high_idx, next_low_idx)) => {
|
||||
Some(high_idx) => {
|
||||
let segment = &line[low_idx..high_idx];
|
||||
|
||||
crash_if_err!(1, out.write_all(segment));
|
||||
out.write_all(segment)?;
|
||||
|
||||
print_delim = true;
|
||||
low_idx = next_low_idx;
|
||||
low_idx = high_idx;
|
||||
fields_pos = high + 1;
|
||||
}
|
||||
None => {
|
||||
let segment = &line[low_idx..line.len()];
|
||||
|
||||
crash_if_err!(1, out.write_all(segment));
|
||||
out.write_all(segment)?;
|
||||
|
||||
if line[line.len() - 1] == newline_char {
|
||||
continue 'newline;
|
||||
return Ok(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crash_if_err!(1, out.write_all(&[newline_char]));
|
||||
}
|
||||
|
||||
out.write_all(&[newline_char])?;
|
||||
Ok(true)
|
||||
});
|
||||
crash_if_err!(1, result);
|
||||
0
|
||||
}
|
||||
|
||||
|
||||
+74
-21
@@ -5,7 +5,8 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
#[derive(Clone)]
|
||||
use memchr::memchr;
|
||||
|
||||
pub struct Searcher<'a> {
|
||||
haystack: &'a [u8],
|
||||
needle: &'a [u8],
|
||||
@@ -14,6 +15,7 @@ pub struct Searcher<'a> {
|
||||
|
||||
impl<'a> Searcher<'a> {
|
||||
pub fn new(haystack: &'a [u8], needle: &'a [u8]) -> Searcher<'a> {
|
||||
assert!(!needle.is_empty());
|
||||
Searcher {
|
||||
haystack,
|
||||
needle,
|
||||
@@ -23,30 +25,81 @@ impl<'a> Searcher<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Searcher<'a> {
|
||||
type Item = (usize, usize);
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<(usize, usize)> {
|
||||
if self.needle.len() == 1 {
|
||||
for offset in self.position..self.haystack.len() {
|
||||
if self.haystack[offset] == self.needle[0] {
|
||||
self.position = offset + 1;
|
||||
return Some((offset, offset + 1));
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some(match_idx) = memchr(self.needle[0], self.haystack) {
|
||||
if self.needle.len() == 1
|
||||
|| self.haystack[match_idx + 1..].starts_with(&self.needle[1..])
|
||||
{
|
||||
let match_pos = self.position + match_idx;
|
||||
let skip = match_idx + self.needle.len();
|
||||
self.haystack = &self.haystack[skip..];
|
||||
self.position += skip;
|
||||
return Some(match_pos);
|
||||
} else {
|
||||
let skip = match_idx + 1;
|
||||
self.haystack = &self.haystack[skip..];
|
||||
self.position += skip;
|
||||
// continue
|
||||
}
|
||||
}
|
||||
|
||||
self.position = self.haystack.len();
|
||||
return None;
|
||||
}
|
||||
|
||||
while self.position + self.needle.len() <= self.haystack.len() {
|
||||
if &self.haystack[self.position..self.position + self.needle.len()] == self.needle {
|
||||
let match_pos = self.position;
|
||||
self.position += self.needle.len();
|
||||
return Some((match_pos, match_pos + self.needle.len()));
|
||||
} else {
|
||||
self.position += 1;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
const NEEDLE: &[u8] = "ab".as_bytes();
|
||||
|
||||
#[test]
|
||||
fn test_normal() {
|
||||
let iter = Searcher::new("a.a.a".as_bytes(), "a".as_bytes());
|
||||
let items: Vec<usize> = iter.collect();
|
||||
assert_eq!(vec![0, 2, 4], items);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
let iter = Searcher::new("".as_bytes(), "a".as_bytes());
|
||||
let items: Vec<usize> = iter.collect();
|
||||
assert_eq!(vec![] as Vec<usize>, items);
|
||||
}
|
||||
|
||||
fn test_multibyte(line: &[u8], expected: Vec<usize>) {
|
||||
let iter = Searcher::new(line, NEEDLE);
|
||||
let items: Vec<usize> = iter.collect();
|
||||
assert_eq!(expected, items);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multibyte_normal() {
|
||||
test_multibyte("...ab...ab...".as_bytes(), vec![3, 8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multibyte_needle_head_at_end() {
|
||||
test_multibyte("a".as_bytes(), vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multibyte_starting_needle() {
|
||||
test_multibyte("ab...ab...".as_bytes(), vec![0, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multibyte_trailing_needle() {
|
||||
test_multibyte("...ab...ab".as_bytes(), vec![3, 8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multibyte_first_byte_false_match() {
|
||||
test_multibyte("aA..aCaC..ab..aD".as_bytes(), vec![10]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
|
||||
#[cfg(target_os = "macos")]
|
||||
fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
|
||||
eprintln!("date: setting the date is not supported by macOS");
|
||||
return 1;
|
||||
1
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
|
||||
@@ -916,6 +916,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
"Use%",
|
||||
]
|
||||
});
|
||||
if cfg!(target_os = "macos") && !opt.show_inode_instead {
|
||||
header.insert(header.len() - 1, "Capacity");
|
||||
}
|
||||
header.push("Mounted on");
|
||||
|
||||
for (idx, title) in header.iter().enumerate() {
|
||||
@@ -970,6 +973,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
"{0: >12} ",
|
||||
human_readable(free_size, opt.human_readable_base)
|
||||
);
|
||||
if cfg!(target_os = "macos") {
|
||||
let used = fs.usage.blocks - fs.usage.bfree;
|
||||
let blocks = used + fs.usage.bavail;
|
||||
print!("{0: >12} ", use_size(used, blocks));
|
||||
}
|
||||
print!("{0: >5} ", use_size(free_size, total_size));
|
||||
}
|
||||
print!("{0: <16}", fs.mountinfo.mount_dir);
|
||||
|
||||
@@ -12,37 +12,42 @@ use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static NAME: &str = "dirname";
|
||||
static SYNTAX: &str = "[OPTION] NAME...";
|
||||
static SUMMARY: &str = "strip last component from file name";
|
||||
static ABOUT: &str = "strip last component from file name";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static LONG_HELP: &str = "
|
||||
Output each NAME with its last non-slash component and trailing slashes
|
||||
removed; if NAME contains no /'s, output '.' (meaning the current
|
||||
directory).
|
||||
";
|
||||
|
||||
mod options {
|
||||
pub const ZERO: &str = "zero";
|
||||
pub const DIR: &str = "dir";
|
||||
}
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} [OPTION] NAME...", executable!())
|
||||
}
|
||||
|
||||
fn get_long_usage() -> String {
|
||||
String::from(
|
||||
"Output each NAME with its last non-slash component and trailing slashes
|
||||
removed; if NAME contains no /'s, output '.' (meaning the current directory).",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let args = args
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
|
||||
let usage = get_usage();
|
||||
let after_help = get_long_usage();
|
||||
|
||||
let matches = App::new(executable!())
|
||||
.name(NAME)
|
||||
.usage(SYNTAX)
|
||||
.about(SUMMARY)
|
||||
.after_help(LONG_HELP)
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.after_help(&after_help[..])
|
||||
.version(VERSION)
|
||||
.arg(
|
||||
Arg::with_name(options::ZERO)
|
||||
.short(options::ZERO)
|
||||
.long(options::ZERO)
|
||||
.short("z")
|
||||
.takes_value(false)
|
||||
.help("separate output with NUL rather than newline"),
|
||||
)
|
||||
.arg(Arg::with_name(options::DIR).hidden(true).multiple(true))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user