mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge branch 'master' into who_fix_runlevel
This commit is contained in:
commit
7bf076505f
20 changed files with 558 additions and 235 deletions
|
@ -16,7 +16,7 @@ path = "src/head.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["ringbuffer"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|
|
@ -27,8 +27,12 @@ mod options {
|
||||||
pub const ZERO_NAME: &str = "ZERO";
|
pub const ZERO_NAME: &str = "ZERO";
|
||||||
pub const FILES_NAME: &str = "FILE";
|
pub const FILES_NAME: &str = "FILE";
|
||||||
}
|
}
|
||||||
|
mod lines;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod split;
|
mod split;
|
||||||
|
mod take;
|
||||||
|
use lines::zlines;
|
||||||
|
use take::take_all_but;
|
||||||
|
|
||||||
fn app<'a>() -> App<'a, 'a> {
|
fn app<'a>() -> App<'a, 'a> {
|
||||||
App::new(executable!())
|
App::new(executable!())
|
||||||
|
@ -293,36 +297,22 @@ fn rbuf_but_last_n_bytes(input: &mut impl std::io::BufRead, n: usize) -> std::io
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rbuf_but_last_n_lines(
|
fn rbuf_but_last_n_lines(
|
||||||
input: &mut impl std::io::BufRead,
|
input: impl std::io::BufRead,
|
||||||
n: usize,
|
n: usize,
|
||||||
zero: bool,
|
zero: bool,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
if n == 0 {
|
if zero {
|
||||||
//prints everything
|
|
||||||
return rbuf_n_bytes(input, std::usize::MAX);
|
|
||||||
}
|
|
||||||
let mut ringbuf = vec![Vec::new(); n];
|
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
let mut stdout = stdout.lock();
|
let mut stdout = stdout.lock();
|
||||||
let mut line = Vec::new();
|
for bytes in take_all_but(zlines(input), n) {
|
||||||
let mut lines = 0usize;
|
stdout.write_all(&bytes?)?;
|
||||||
split::walk_lines(input, zero, |e| match e {
|
|
||||||
split::Event::Data(dat) => {
|
|
||||||
line.extend_from_slice(dat);
|
|
||||||
Ok(true)
|
|
||||||
}
|
}
|
||||||
split::Event::Line => {
|
|
||||||
if lines < n {
|
|
||||||
ringbuf[lines] = std::mem::replace(&mut line, Vec::new());
|
|
||||||
lines += 1;
|
|
||||||
} else {
|
} else {
|
||||||
stdout.write_all(&ringbuf[0])?;
|
for line in take_all_but(input.lines(), n) {
|
||||||
ringbuf.rotate_left(1);
|
println!("{}", line?);
|
||||||
ringbuf[n - 1] = std::mem::replace(&mut line, Vec::new());
|
|
||||||
}
|
}
|
||||||
Ok(true)
|
|
||||||
}
|
}
|
||||||
})
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head_backwards_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Result<()> {
|
fn head_backwards_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Result<()> {
|
||||||
|
|
73
src/uu/head/src/lines.rs
Normal file
73
src/uu/head/src/lines.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
//! Iterate over zero-terminated lines.
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
|
/// The zero byte, representing the null character.
|
||||||
|
const ZERO: u8 = 0;
|
||||||
|
|
||||||
|
/// Returns an iterator over the lines of the given reader.
|
||||||
|
///
|
||||||
|
/// The iterator returned from this function will yield instances of
|
||||||
|
/// [`io::Result`]<[`Vec`]<[`u8`]>>, representing the bytes of the line
|
||||||
|
/// *including* the null character (with the possible exception of the
|
||||||
|
/// last line, which may not have one).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let cursor = Cursor::new(b"x\0y\0z\0");
|
||||||
|
/// let mut iter = zlines(cursor).map(|l| l.unwrap());
|
||||||
|
/// assert_eq!(iter.next(), Some(b"x\0".to_vec()));
|
||||||
|
/// assert_eq!(iter.next(), Some(b"y\0".to_vec()));
|
||||||
|
/// assert_eq!(iter.next(), Some(b"z\0".to_vec()));
|
||||||
|
/// assert_eq!(iter.next(), None);
|
||||||
|
/// ```
|
||||||
|
pub fn zlines<B>(buf: B) -> ZLines<B> {
|
||||||
|
ZLines { buf }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator over the zero-terminated lines of an instance of `BufRead`.
|
||||||
|
pub struct ZLines<B> {
|
||||||
|
buf: B,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BufRead> Iterator for ZLines<B> {
|
||||||
|
type Item = std::io::Result<Vec<u8>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
match self.buf.read_until(ZERO, &mut buf) {
|
||||||
|
Ok(0) => None,
|
||||||
|
Ok(_) => Some(Ok(buf)),
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::lines::zlines;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_null_terminated() {
|
||||||
|
let cursor = Cursor::new(b"x\0y\0z\0");
|
||||||
|
let mut iter = zlines(cursor).map(|l| l.unwrap());
|
||||||
|
assert_eq!(iter.next(), Some(b"x\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"y\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"z\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_null_terminated() {
|
||||||
|
let cursor = Cursor::new(b"x\0y\0z");
|
||||||
|
let mut iter = zlines(cursor).map(|l| l.unwrap());
|
||||||
|
assert_eq!(iter.next(), Some(b"x\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"y\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"z".to_vec()));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
}
|
93
src/uu/head/src/take.rs
Normal file
93
src/uu/head/src/take.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
//! Take all but the last elements of an iterator.
|
||||||
|
use uucore::ringbuffer::RingBuffer;
|
||||||
|
|
||||||
|
/// Create an iterator over all but the last `n` elements of `iter`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let data = [1, 2, 3, 4, 5];
|
||||||
|
/// let n = 2;
|
||||||
|
/// let mut iter = take_all_but(data.iter(), n);
|
||||||
|
/// assert_eq!(Some(4), iter.next());
|
||||||
|
/// assert_eq!(Some(5), iter.next());
|
||||||
|
/// assert_eq!(None, iter.next());
|
||||||
|
/// ```
|
||||||
|
pub fn take_all_but<I: Iterator>(iter: I, n: usize) -> TakeAllBut<I> {
|
||||||
|
TakeAllBut::new(iter, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator that only iterates over the last elements of another iterator.
|
||||||
|
pub struct TakeAllBut<I: Iterator> {
|
||||||
|
iter: I,
|
||||||
|
buf: RingBuffer<<I as Iterator>::Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator> TakeAllBut<I> {
|
||||||
|
pub fn new(mut iter: I, n: usize) -> TakeAllBut<I> {
|
||||||
|
// Create a new ring buffer and fill it up.
|
||||||
|
//
|
||||||
|
// If there are fewer than `n` elements in `iter`, then we
|
||||||
|
// exhaust the iterator so that whenever `TakeAllBut::next()` is
|
||||||
|
// called, it will return `None`, as expected.
|
||||||
|
let mut buf = RingBuffer::new(n);
|
||||||
|
for _ in 0..n {
|
||||||
|
let value = match iter.next() {
|
||||||
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(x) => x,
|
||||||
|
};
|
||||||
|
buf.push_back(value);
|
||||||
|
}
|
||||||
|
TakeAllBut { iter, buf }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator> Iterator for TakeAllBut<I>
|
||||||
|
where
|
||||||
|
I: Iterator,
|
||||||
|
{
|
||||||
|
type Item = <I as Iterator>::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<<I as Iterator>::Item> {
|
||||||
|
match self.iter.next() {
|
||||||
|
Some(value) => self.buf.push_back(value),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::take::take_all_but;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fewer_elements() {
|
||||||
|
let mut iter = take_all_but([0, 1, 2].iter(), 2);
|
||||||
|
assert_eq!(Some(&0), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_same_number_of_elements() {
|
||||||
|
let mut iter = take_all_but([0, 1].iter(), 2);
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_more_elements() {
|
||||||
|
let mut iter = take_all_but([0].iter(), 2);
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_elements() {
|
||||||
|
let mut iter = take_all_but([0, 1, 2].iter(), 0);
|
||||||
|
assert_eq!(Some(&0), iter.next());
|
||||||
|
assert_eq!(Some(&1), iter.next());
|
||||||
|
assert_eq!(Some(&2), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
}
|
|
@ -223,9 +223,7 @@ fn read_to_buffer(
|
||||||
Err(e) if e.kind() == ErrorKind::Interrupted => {
|
Err(e) if e.kind() == ErrorKind::Interrupted => {
|
||||||
// retry
|
// retry
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => crash!(1, "{}", e),
|
||||||
crash!(1, "{}", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,17 @@ static OPT_NUMERIC_SORT: &str = "numeric-sort";
|
||||||
static OPT_GENERAL_NUMERIC_SORT: &str = "general-numeric-sort";
|
static OPT_GENERAL_NUMERIC_SORT: &str = "general-numeric-sort";
|
||||||
static OPT_VERSION_SORT: &str = "version-sort";
|
static OPT_VERSION_SORT: &str = "version-sort";
|
||||||
|
|
||||||
|
static OPT_SORT: &str = "sort";
|
||||||
|
|
||||||
|
static ALL_SORT_MODES: &[&str] = &[
|
||||||
|
OPT_GENERAL_NUMERIC_SORT,
|
||||||
|
OPT_HUMAN_NUMERIC_SORT,
|
||||||
|
OPT_MONTH_SORT,
|
||||||
|
OPT_NUMERIC_SORT,
|
||||||
|
OPT_VERSION_SORT,
|
||||||
|
OPT_RANDOM,
|
||||||
|
];
|
||||||
|
|
||||||
static OPT_DICTIONARY_ORDER: &str = "dictionary-order";
|
static OPT_DICTIONARY_ORDER: &str = "dictionary-order";
|
||||||
static OPT_MERGE: &str = "merge";
|
static OPT_MERGE: &str = "merge";
|
||||||
static OPT_CHECK: &str = "check";
|
static OPT_CHECK: &str = "check";
|
||||||
|
@ -105,6 +116,7 @@ enum SortMode {
|
||||||
GeneralNumeric,
|
GeneralNumeric,
|
||||||
Month,
|
Month,
|
||||||
Version,
|
Version,
|
||||||
|
Random,
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -122,7 +134,6 @@ pub struct GlobalSettings {
|
||||||
unique: bool,
|
unique: bool,
|
||||||
check: bool,
|
check: bool,
|
||||||
check_silent: bool,
|
check_silent: bool,
|
||||||
random: bool,
|
|
||||||
salt: String,
|
salt: String,
|
||||||
selectors: Vec<FieldSelector>,
|
selectors: Vec<FieldSelector>,
|
||||||
separator: Option<char>,
|
separator: Option<char>,
|
||||||
|
@ -191,7 +202,6 @@ impl Default for GlobalSettings {
|
||||||
unique: false,
|
unique: false,
|
||||||
check: false,
|
check: false,
|
||||||
check_silent: false,
|
check_silent: false,
|
||||||
random: false,
|
|
||||||
salt: String::new(),
|
salt: String::new(),
|
||||||
selectors: vec![],
|
selectors: vec![],
|
||||||
separator: None,
|
separator: None,
|
||||||
|
@ -209,7 +219,6 @@ struct KeySettings {
|
||||||
ignore_case: bool,
|
ignore_case: bool,
|
||||||
dictionary_order: bool,
|
dictionary_order: bool,
|
||||||
ignore_non_printing: bool,
|
ignore_non_printing: bool,
|
||||||
random: bool,
|
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +229,6 @@ impl From<&GlobalSettings> for KeySettings {
|
||||||
ignore_blanks: settings.ignore_blanks,
|
ignore_blanks: settings.ignore_blanks,
|
||||||
ignore_case: settings.ignore_case,
|
ignore_case: settings.ignore_case,
|
||||||
ignore_non_printing: settings.ignore_non_printing,
|
ignore_non_printing: settings.ignore_non_printing,
|
||||||
random: settings.random,
|
|
||||||
reverse: settings.reverse,
|
reverse: settings.reverse,
|
||||||
dictionary_order: settings.dictionary_order,
|
dictionary_order: settings.dictionary_order,
|
||||||
}
|
}
|
||||||
|
@ -398,7 +406,7 @@ impl<'a> Line<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !(settings.random
|
if !(settings.mode == SortMode::Random
|
||||||
|| settings.stable
|
|| settings.stable
|
||||||
|| settings.unique
|
|| settings.unique
|
||||||
|| !(settings.dictionary_order
|
|| !(settings.dictionary_order
|
||||||
|
@ -502,12 +510,10 @@ impl KeyPosition {
|
||||||
'h' => settings.mode = SortMode::HumanNumeric,
|
'h' => settings.mode = SortMode::HumanNumeric,
|
||||||
'i' => settings.ignore_non_printing = true,
|
'i' => settings.ignore_non_printing = true,
|
||||||
'n' => settings.mode = SortMode::Numeric,
|
'n' => settings.mode = SortMode::Numeric,
|
||||||
'R' => settings.random = true,
|
'R' => settings.mode = SortMode::Random,
|
||||||
'r' => settings.reverse = true,
|
'r' => settings.reverse = true,
|
||||||
'V' => settings.mode = SortMode::Version,
|
'V' => settings.mode = SortMode::Version,
|
||||||
c => {
|
c => crash!(1, "invalid option for key: `{}`", c),
|
||||||
crash!(1, "invalid option for key: `{}`", c)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// All numeric sorts and month sort conflict with dictionary_order and ignore_non_printing.
|
// All numeric sorts and month sort conflict with dictionary_order and ignore_non_printing.
|
||||||
// Instad of reporting an error, let them overwrite each other.
|
// Instad of reporting an error, let them overwrite each other.
|
||||||
|
@ -526,7 +532,9 @@ impl KeyPosition {
|
||||||
| SortMode::GeneralNumeric
|
| SortMode::GeneralNumeric
|
||||||
| SortMode::Month => SortMode::Default,
|
| SortMode::Month => SortMode::Default,
|
||||||
// Only SortMode::Default and SortMode::Version work with dictionary_order and ignore_non_printing
|
// Only SortMode::Default and SortMode::Version work with dictionary_order and ignore_non_printing
|
||||||
m @ SortMode::Default | m @ SortMode::Version => m,
|
m @ SortMode::Default
|
||||||
|
| m @ SortMode::Version
|
||||||
|
| m @ SortMode::Random => m,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -720,6 +728,16 @@ With no FILE, or when FILE is -, read standard input.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_sort_mode_arg<'a, 'b>(mode: &'a str, short: &'b str, help: &'b str) -> Arg<'a, 'b> {
|
||||||
|
let mut arg = Arg::with_name(mode).short(short).long(mode).help(help);
|
||||||
|
for possible_mode in ALL_SORT_MODES {
|
||||||
|
if *possible_mode != mode {
|
||||||
|
arg = arg.conflicts_with(possible_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let args = args
|
let args = args
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
|
@ -732,34 +750,62 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.usage(&usage[..])
|
.usage(&usage[..])
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_HUMAN_NUMERIC_SORT)
|
Arg::with_name(OPT_SORT)
|
||||||
.short("h")
|
.long(OPT_SORT)
|
||||||
.long(OPT_HUMAN_NUMERIC_SORT)
|
.takes_value(true)
|
||||||
.help("compare according to human readable sizes, eg 1M > 100k"),
|
.possible_values(
|
||||||
|
&[
|
||||||
|
"general-numeric",
|
||||||
|
"human-numeric",
|
||||||
|
"month",
|
||||||
|
"numeric",
|
||||||
|
"version",
|
||||||
|
"random",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.conflicts_with_all(ALL_SORT_MODES)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_MONTH_SORT)
|
make_sort_mode_arg(
|
||||||
.short("M")
|
OPT_HUMAN_NUMERIC_SORT,
|
||||||
.long(OPT_MONTH_SORT)
|
"h",
|
||||||
.help("compare according to month name abbreviation"),
|
"compare according to human readable sizes, eg 1M > 100k"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_NUMERIC_SORT)
|
make_sort_mode_arg(
|
||||||
.short("n")
|
OPT_MONTH_SORT,
|
||||||
.long(OPT_NUMERIC_SORT)
|
"M",
|
||||||
.help("compare according to string numerical value"),
|
"compare according to month name abbreviation"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_GENERAL_NUMERIC_SORT)
|
make_sort_mode_arg(
|
||||||
.short("g")
|
OPT_NUMERIC_SORT,
|
||||||
.long(OPT_GENERAL_NUMERIC_SORT)
|
"n",
|
||||||
.help("compare according to string general numerical value"),
|
"compare according to string numerical value"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_VERSION_SORT)
|
make_sort_mode_arg(
|
||||||
.short("V")
|
OPT_GENERAL_NUMERIC_SORT,
|
||||||
.long(OPT_VERSION_SORT)
|
"g",
|
||||||
.help("Sort by SemVer version number, eg 1.12.2 > 1.1.2"),
|
"compare according to string general numerical value"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
make_sort_mode_arg(
|
||||||
|
OPT_VERSION_SORT,
|
||||||
|
"V",
|
||||||
|
"Sort by SemVer version number, eg 1.12.2 > 1.1.2",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
make_sort_mode_arg(
|
||||||
|
OPT_RANDOM,
|
||||||
|
"R",
|
||||||
|
"shuffle in random order",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_DICTIONARY_ORDER)
|
Arg::with_name(OPT_DICTIONARY_ORDER)
|
||||||
|
@ -813,12 +859,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("FILENAME"),
|
.value_name("FILENAME"),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name(OPT_RANDOM)
|
|
||||||
.short("R")
|
|
||||||
.long(OPT_RANDOM)
|
|
||||||
.help("shuffle in random order"),
|
|
||||||
)
|
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(OPT_REVERSE)
|
Arg::with_name(OPT_REVERSE)
|
||||||
.short("r")
|
.short("r")
|
||||||
|
@ -925,16 +965,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
};
|
};
|
||||||
|
|
||||||
settings.mode = if matches.is_present(OPT_HUMAN_NUMERIC_SORT) {
|
settings.mode = if matches.is_present(OPT_HUMAN_NUMERIC_SORT)
|
||||||
|
|| matches.value_of(OPT_SORT) == Some("human-numeric")
|
||||||
|
{
|
||||||
SortMode::HumanNumeric
|
SortMode::HumanNumeric
|
||||||
} else if matches.is_present(OPT_MONTH_SORT) {
|
} else if matches.is_present(OPT_MONTH_SORT) || matches.value_of(OPT_SORT) == Some("month") {
|
||||||
SortMode::Month
|
SortMode::Month
|
||||||
} else if matches.is_present(OPT_GENERAL_NUMERIC_SORT) {
|
} else if matches.is_present(OPT_GENERAL_NUMERIC_SORT)
|
||||||
|
|| matches.value_of(OPT_SORT) == Some("general-numeric")
|
||||||
|
{
|
||||||
SortMode::GeneralNumeric
|
SortMode::GeneralNumeric
|
||||||
} else if matches.is_present(OPT_NUMERIC_SORT) {
|
} else if matches.is_present(OPT_NUMERIC_SORT) || matches.value_of(OPT_SORT) == Some("numeric")
|
||||||
|
{
|
||||||
SortMode::Numeric
|
SortMode::Numeric
|
||||||
} else if matches.is_present(OPT_VERSION_SORT) {
|
} else if matches.is_present(OPT_VERSION_SORT) || matches.value_of(OPT_SORT) == Some("version")
|
||||||
|
{
|
||||||
SortMode::Version
|
SortMode::Version
|
||||||
|
} else if matches.is_present(OPT_RANDOM) || matches.value_of(OPT_SORT) == Some("random") {
|
||||||
|
settings.salt = get_rand_string();
|
||||||
|
SortMode::Random
|
||||||
} else {
|
} else {
|
||||||
SortMode::Default
|
SortMode::Default
|
||||||
};
|
};
|
||||||
|
@ -978,11 +1027,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
settings.stable = matches.is_present(OPT_STABLE);
|
settings.stable = matches.is_present(OPT_STABLE);
|
||||||
settings.unique = matches.is_present(OPT_UNIQUE);
|
settings.unique = matches.is_present(OPT_UNIQUE);
|
||||||
|
|
||||||
if matches.is_present(OPT_RANDOM) {
|
|
||||||
settings.random = matches.is_present(OPT_RANDOM);
|
|
||||||
settings.salt = get_rand_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if files.is_empty() {
|
if files.is_empty() {
|
||||||
/* if no file, default to stdin */
|
/* if no file, default to stdin */
|
||||||
files.push("-".to_owned());
|
files.push("-".to_owned());
|
||||||
|
@ -1110,10 +1154,8 @@ fn compare_by<'a>(a: &Line<'a>, b: &Line<'a>, global_settings: &GlobalSettings)
|
||||||
let b_str = b_selection.slice;
|
let b_str = b_selection.slice;
|
||||||
let settings = &selector.settings;
|
let settings = &selector.settings;
|
||||||
|
|
||||||
let cmp: Ordering = if settings.random {
|
let cmp: Ordering = match settings.mode {
|
||||||
random_shuffle(a_str, b_str, &global_settings.salt)
|
SortMode::Random => random_shuffle(a_str, b_str, &global_settings.salt),
|
||||||
} else {
|
|
||||||
match settings.mode {
|
|
||||||
SortMode::Numeric | SortMode::HumanNumeric => numeric_str_cmp(
|
SortMode::Numeric | SortMode::HumanNumeric => numeric_str_cmp(
|
||||||
(a_str, a_selection.num_cache.as_ref().unwrap().as_num_info()),
|
(a_str, a_selection.num_cache.as_ref().unwrap().as_num_info()),
|
||||||
(b_str, b_selection.num_cache.as_ref().unwrap().as_num_info()),
|
(b_str, b_selection.num_cache.as_ref().unwrap().as_num_info()),
|
||||||
|
@ -1131,7 +1173,6 @@ fn compare_by<'a>(a: &Line<'a>, b: &Line<'a>, global_settings: &GlobalSettings)
|
||||||
settings.dictionary_order,
|
settings.dictionary_order,
|
||||||
settings.ignore_case,
|
settings.ignore_case,
|
||||||
),
|
),
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if cmp != Ordering::Equal {
|
if cmp != Ordering::Equal {
|
||||||
return if settings.reverse { cmp.reverse() } else { cmp };
|
return if settings.reverse { cmp.reverse() } else { cmp };
|
||||||
|
@ -1139,7 +1180,10 @@ fn compare_by<'a>(a: &Line<'a>, b: &Line<'a>, global_settings: &GlobalSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call "last resort compare" if all selectors returned Equal
|
// Call "last resort compare" if all selectors returned Equal
|
||||||
let cmp = if global_settings.random || global_settings.stable || global_settings.unique {
|
let cmp = if global_settings.mode == SortMode::Random
|
||||||
|
|| global_settings.stable
|
||||||
|
|| global_settings.unique
|
||||||
|
{
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else {
|
} else {
|
||||||
a.line.cmp(b.line)
|
a.line.cmp(b.line)
|
||||||
|
|
|
@ -25,7 +25,8 @@ static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
static ABOUT: &str =
|
static ABOUT: &str =
|
||||||
"Run COMMAND, with modified buffering operations for its standard streams.\n\n\
|
"Run COMMAND, with modified buffering operations for its standard streams.\n\n\
|
||||||
Mandatory arguments to long options are mandatory for short options too.";
|
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\
|
This option is invalid with standard input.\n\n\
|
||||||
If MODE is '0' the corresponding stream will be unbuffered.\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\
|
Otherwise MODE is a number which may be followed by one of the following:\n\n\
|
||||||
|
|
|
@ -17,7 +17,7 @@ path = "src/tail.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["ringbuffer"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] }
|
winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] }
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
//! A fixed-size ring buffer.
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
/// A fixed-size ring buffer backed by a `VecDeque`.
|
|
||||||
///
|
|
||||||
/// If the ring buffer is not full, then calling the [`push_back`]
|
|
||||||
/// method appends elements, as in a [`VecDeque`]. If the ring buffer
|
|
||||||
/// is full, then calling [`push_back`] removes the element at the
|
|
||||||
/// front of the buffer (in a first-in, first-out manner) before
|
|
||||||
/// appending the new element to the back of the buffer.
|
|
||||||
///
|
|
||||||
/// Use [`from_iter`] to take the last `size` elements from an
|
|
||||||
/// iterator.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// After exceeding the size limit, the oldest elements are dropped in
|
|
||||||
/// favor of the newest element:
|
|
||||||
///
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// let buffer: RingBuffer<u8> = RingBuffer::new(2);
|
|
||||||
/// buffer.push_back(0);
|
|
||||||
/// buffer.push_back(1);
|
|
||||||
/// buffer.push_back(2);
|
|
||||||
/// assert_eq!(vec![1, 2], buffer.data);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Take the last `n` elements from an iterator:
|
|
||||||
///
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// let iter = vec![0, 1, 2, 3].iter();
|
|
||||||
/// assert_eq!(vec![2, 3], RingBuffer::from_iter(iter, 2).data);
|
|
||||||
/// ```
|
|
||||||
pub struct RingBuffer<T> {
|
|
||||||
pub data: VecDeque<T>,
|
|
||||||
size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> RingBuffer<T> {
|
|
||||||
pub fn new(size: usize) -> RingBuffer<T> {
|
|
||||||
RingBuffer {
|
|
||||||
data: VecDeque::new(),
|
|
||||||
size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_iter(iter: impl Iterator<Item = T>, size: usize) -> RingBuffer<T> {
|
|
||||||
let mut ringbuf = RingBuffer::new(size);
|
|
||||||
for value in iter {
|
|
||||||
ringbuf.push_back(value);
|
|
||||||
}
|
|
||||||
ringbuf
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_back(&mut self, value: T) {
|
|
||||||
if self.size <= self.data.len() {
|
|
||||||
self.data.pop_front();
|
|
||||||
}
|
|
||||||
self.data.push_back(value)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,9 +17,7 @@ extern crate uucore;
|
||||||
|
|
||||||
mod chunks;
|
mod chunks;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod ringbuffer;
|
|
||||||
use chunks::ReverseChunks;
|
use chunks::ReverseChunks;
|
||||||
use ringbuffer::RingBuffer;
|
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -30,6 +28,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use uucore::ringbuffer::RingBuffer;
|
||||||
|
|
||||||
pub mod options {
|
pub mod options {
|
||||||
pub mod verbosity {
|
pub mod verbosity {
|
||||||
|
|
|
@ -47,6 +47,7 @@ mode = ["libc"]
|
||||||
parse_time = []
|
parse_time = []
|
||||||
perms = ["libc"]
|
perms = ["libc"]
|
||||||
process = ["libc"]
|
process = ["libc"]
|
||||||
|
ringbuffer = []
|
||||||
signals = []
|
signals = []
|
||||||
utf8 = []
|
utf8 = []
|
||||||
utmpx = ["time", "libc"]
|
utmpx = ["time", "libc"]
|
||||||
|
|
|
@ -8,6 +8,8 @@ pub mod fs;
|
||||||
pub mod fsext;
|
pub mod fsext;
|
||||||
#[cfg(feature = "parse_time")]
|
#[cfg(feature = "parse_time")]
|
||||||
pub mod parse_time;
|
pub mod parse_time;
|
||||||
|
#[cfg(feature = "ringbuffer")]
|
||||||
|
pub mod ringbuffer;
|
||||||
#[cfg(feature = "zero-copy")]
|
#[cfg(feature = "zero-copy")]
|
||||||
pub mod zero_copy;
|
pub mod zero_copy;
|
||||||
|
|
||||||
|
|
134
src/uucore/src/lib/features/ringbuffer.rs
Normal file
134
src/uucore/src/lib/features/ringbuffer.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
//! A fixed-size ring buffer.
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
/// A fixed-size ring buffer backed by a `VecDeque`.
|
||||||
|
///
|
||||||
|
/// If the ring buffer is not full, then calling the [`push_back`]
|
||||||
|
/// method appends elements, as in a [`VecDeque`]. If the ring buffer
|
||||||
|
/// is full, then calling [`push_back`] removes the element at the
|
||||||
|
/// front of the buffer (in a first-in, first-out manner) before
|
||||||
|
/// appending the new element to the back of the buffer.
|
||||||
|
///
|
||||||
|
/// Use [`from_iter`] to take the last `size` elements from an
|
||||||
|
/// iterator.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// After exceeding the size limit, the oldest elements are dropped in
|
||||||
|
/// favor of the newest element:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let mut buffer: RingBuffer<u8> = RingBuffer::new(2);
|
||||||
|
/// buffer.push_back(0);
|
||||||
|
/// buffer.push_back(1);
|
||||||
|
/// buffer.push_back(2);
|
||||||
|
/// assert_eq!(vec![1, 2], buffer.data);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Take the last `n` elements from an iterator:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let iter = [0, 1, 2].iter();
|
||||||
|
/// let actual = RingBuffer::from_iter(iter, 2).data;
|
||||||
|
/// let expected = VecDeque::from_iter([1, 2].iter());
|
||||||
|
/// assert_eq!(expected, actual);
|
||||||
|
/// ```
|
||||||
|
pub struct RingBuffer<T> {
|
||||||
|
pub data: VecDeque<T>,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> RingBuffer<T> {
|
||||||
|
pub fn new(size: usize) -> RingBuffer<T> {
|
||||||
|
RingBuffer {
|
||||||
|
data: VecDeque::new(),
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_iter(iter: impl Iterator<Item = T>, size: usize) -> RingBuffer<T> {
|
||||||
|
let mut ringbuf = RingBuffer::new(size);
|
||||||
|
for value in iter {
|
||||||
|
ringbuf.push_back(value);
|
||||||
|
}
|
||||||
|
ringbuf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a value to the end of the ring buffer.
|
||||||
|
///
|
||||||
|
/// If the ring buffer is not full, this method return [`None`]. If
|
||||||
|
/// the ring buffer is full, appending a new element will cause the
|
||||||
|
/// oldest element to be evicted. In that case this method returns
|
||||||
|
/// that element, or `None`.
|
||||||
|
///
|
||||||
|
/// In the special case where the size limit is zero, each call to
|
||||||
|
/// this method with input `value` returns `Some(value)`, because
|
||||||
|
/// the input is immediately evicted.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Appending an element when the buffer is full returns the oldest
|
||||||
|
/// element:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let mut buf = RingBuffer::new(3);
|
||||||
|
/// assert_eq!(None, buf.push_back(0));
|
||||||
|
/// assert_eq!(None, buf.push_back(1));
|
||||||
|
/// assert_eq!(None, buf.push_back(2));
|
||||||
|
/// assert_eq!(Some(0), buf.push_back(3));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If the size limit is zero, then this method always returns the
|
||||||
|
/// input value:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let mut buf = RingBuffer::new(0);
|
||||||
|
/// assert_eq!(Some(0), buf.push_back(0));
|
||||||
|
/// assert_eq!(Some(1), buf.push_back(1));
|
||||||
|
/// assert_eq!(Some(2), buf.push_back(2));
|
||||||
|
/// ```
|
||||||
|
pub fn push_back(&mut self, value: T) -> Option<T> {
|
||||||
|
if self.size == 0 {
|
||||||
|
return Some(value);
|
||||||
|
}
|
||||||
|
let result = if self.size <= self.data.len() {
|
||||||
|
self.data.pop_front()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self.data.push_back(value);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::ringbuffer::RingBuffer;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_size_limit_zero() {
|
||||||
|
let mut buf = RingBuffer::new(0);
|
||||||
|
assert_eq!(Some(0), buf.push_back(0));
|
||||||
|
assert_eq!(Some(1), buf.push_back(1));
|
||||||
|
assert_eq!(Some(2), buf.push_back(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_evict_oldest() {
|
||||||
|
let mut buf = RingBuffer::new(2);
|
||||||
|
assert_eq!(None, buf.push_back(0));
|
||||||
|
assert_eq!(None, buf.push_back(1));
|
||||||
|
assert_eq!(Some(0), buf.push_back(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_iter() {
|
||||||
|
let iter = [0, 1, 2].iter();
|
||||||
|
let actual = RingBuffer::from_iter(iter, 2).data;
|
||||||
|
let expected = VecDeque::from_iter([1, 2].iter());
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,8 @@ pub use crate::features::fs;
|
||||||
pub use crate::features::fsext;
|
pub use crate::features::fsext;
|
||||||
#[cfg(feature = "parse_time")]
|
#[cfg(feature = "parse_time")]
|
||||||
pub use crate::features::parse_time;
|
pub use crate::features::parse_time;
|
||||||
|
#[cfg(feature = "ringbuffer")]
|
||||||
|
pub use crate::features::ringbuffer;
|
||||||
#[cfg(feature = "zero-copy")]
|
#[cfg(feature = "zero-copy")]
|
||||||
pub use crate::features::zero_copy;
|
pub use crate::features::zero_copy;
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,15 @@ fn test_zero_terminated_syntax_2() {
|
||||||
.stdout_is("x\0y");
|
.stdout_is("x\0y");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_terminated_negative_lines() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-z", "-n", "-1"])
|
||||||
|
.pipe_in("x\0y\0z\0")
|
||||||
|
.run()
|
||||||
|
.stdout_is("x\0y\0");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_negative_byte_syntax() {
|
fn test_negative_byte_syntax() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
|
@ -281,6 +281,7 @@ fn test_leading_whitespace_in_free_argument_should_imply_padding() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn test_should_calculate_implicit_padding_per_free_argument() {
|
fn test_should_calculate_implicit_padding_per_free_argument() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--from=auto", " 1Ki", " 2K"])
|
.args(&["--from=auto", " 1Ki", " 2K"])
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
|
||||||
fn test_helper(file_name: &str, args: &str) {
|
fn test_helper(file_name: &str, possible_args: &[&str]) {
|
||||||
|
for args in possible_args {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.arg(format!("{}.txt", file_name))
|
.arg(format!("{}.txt", file_name))
|
||||||
.args(&args.split(' ').collect::<Vec<&str>>())
|
.args(&args.split(' ').collect::<Vec<&str>>())
|
||||||
|
@ -14,6 +15,7 @@ fn test_helper(file_name: &str, args: &str) {
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is_fixture(format!("{}.expected.debug", file_name));
|
.stdout_is_fixture(format!("{}.expected.debug", file_name));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_buffer_sizes() {
|
fn test_buffer_sizes() {
|
||||||
|
@ -71,7 +73,7 @@ fn test_extsort_zero_terminated() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_months_whitespace() {
|
fn test_months_whitespace() {
|
||||||
test_helper("months-whitespace", "-M");
|
test_helper("months-whitespace", &["-M", "--month-sort", "--sort=month"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -85,7 +87,10 @@ fn test_version_empty_lines() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_human_numeric_whitespace() {
|
fn test_human_numeric_whitespace() {
|
||||||
test_helper("human-numeric-whitespace", "-h");
|
test_helper(
|
||||||
|
"human-numeric-whitespace",
|
||||||
|
&["-h", "--human-numeric-sort", "--sort=human-numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tests where serde often fails when reading back JSON
|
// This tests where serde often fails when reading back JSON
|
||||||
|
@ -102,12 +107,18 @@ fn test_extsort_as64_bailout() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_decimals_general() {
|
fn test_multiple_decimals_general() {
|
||||||
test_helper("multiple_decimals_general", "-g")
|
test_helper(
|
||||||
|
"multiple_decimals_general",
|
||||||
|
&["-g", "--general-numeric-sort", "--sort=general-numeric"],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiple_decimals_numeric() {
|
fn test_multiple_decimals_numeric() {
|
||||||
test_helper("multiple_decimals_numeric", "-n")
|
test_helper(
|
||||||
|
"multiple_decimals_numeric",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -186,72 +197,93 @@ fn test_random_shuffle_contains_two_runs_not_the_same() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_floats_and_ints() {
|
fn test_numeric_floats_and_ints() {
|
||||||
test_helper("numeric_floats_and_ints", "-n");
|
test_helper(
|
||||||
|
"numeric_floats_and_ints",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_floats() {
|
fn test_numeric_floats() {
|
||||||
test_helper("numeric_floats", "-n");
|
test_helper(
|
||||||
|
"numeric_floats",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_floats_with_nan() {
|
fn test_numeric_floats_with_nan() {
|
||||||
test_helper("numeric_floats_with_nan", "-n");
|
test_helper(
|
||||||
|
"numeric_floats_with_nan",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_unfixed_floats() {
|
fn test_numeric_unfixed_floats() {
|
||||||
test_helper("numeric_unfixed_floats", "-n");
|
test_helper(
|
||||||
|
"numeric_unfixed_floats",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_fixed_floats() {
|
fn test_numeric_fixed_floats() {
|
||||||
test_helper("numeric_fixed_floats", "-n");
|
test_helper(
|
||||||
|
"numeric_fixed_floats",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_unsorted_ints() {
|
fn test_numeric_unsorted_ints() {
|
||||||
test_helper("numeric_unsorted_ints", "-n");
|
test_helper(
|
||||||
|
"numeric_unsorted_ints",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_human_block_sizes() {
|
fn test_human_block_sizes() {
|
||||||
test_helper("human_block_sizes", "-h");
|
test_helper(
|
||||||
|
"human_block_sizes",
|
||||||
|
&["-h", "--human-numeric-sort", "--sort=human-numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_month_default() {
|
fn test_month_default() {
|
||||||
test_helper("month_default", "-M");
|
test_helper("month_default", &["-M", "--month-sort", "--sort=month"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_month_stable() {
|
fn test_month_stable() {
|
||||||
test_helper("month_stable", "-Ms");
|
test_helper("month_stable", &["-Ms"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_unsorted_ints() {
|
fn test_default_unsorted_ints() {
|
||||||
test_helper("default_unsorted_ints", "");
|
test_helper("default_unsorted_ints", &[""]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_unique_ints() {
|
fn test_numeric_unique_ints() {
|
||||||
test_helper("numeric_unsorted_ints_unique", "-nu");
|
test_helper("numeric_unsorted_ints_unique", &["-nu"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_version() {
|
fn test_version() {
|
||||||
test_helper("version", "-V");
|
test_helper("version", &["-V"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ignore_case() {
|
fn test_ignore_case() {
|
||||||
test_helper("ignore_case", "-f");
|
test_helper("ignore_case", &["-f"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dictionary_order() {
|
fn test_dictionary_order() {
|
||||||
test_helper("dictionary_order", "-d");
|
test_helper("dictionary_order", &["-d"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -278,47 +310,53 @@ fn test_non_printing_chars() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exponents_positive_general_fixed() {
|
fn test_exponents_positive_general_fixed() {
|
||||||
test_helper("exponents_general", "-g");
|
test_helper("exponents_general", &["-g"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exponents_positive_numeric() {
|
fn test_exponents_positive_numeric() {
|
||||||
test_helper("exponents-positive-numeric", "-n");
|
test_helper(
|
||||||
|
"exponents-positive-numeric",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_months_dedup() {
|
fn test_months_dedup() {
|
||||||
test_helper("months-dedup", "-Mu");
|
test_helper("months-dedup", &["-Mu"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mixed_floats_ints_chars_numeric() {
|
fn test_mixed_floats_ints_chars_numeric() {
|
||||||
test_helper("mixed_floats_ints_chars_numeric", "-n");
|
test_helper(
|
||||||
|
"mixed_floats_ints_chars_numeric",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mixed_floats_ints_chars_numeric_unique() {
|
fn test_mixed_floats_ints_chars_numeric_unique() {
|
||||||
test_helper("mixed_floats_ints_chars_numeric_unique", "-nu");
|
test_helper("mixed_floats_ints_chars_numeric_unique", &["-nu"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_words_unique() {
|
fn test_words_unique() {
|
||||||
test_helper("words_unique", "-u");
|
test_helper("words_unique", &["-u"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_unique() {
|
fn test_numeric_unique() {
|
||||||
test_helper("numeric_unique", "-nu");
|
test_helper("numeric_unique", &["-nu"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mixed_floats_ints_chars_numeric_reverse() {
|
fn test_mixed_floats_ints_chars_numeric_reverse() {
|
||||||
test_helper("mixed_floats_ints_chars_numeric_unique_reverse", "-nur");
|
test_helper("mixed_floats_ints_chars_numeric_unique_reverse", &["-nur"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mixed_floats_ints_chars_numeric_stable() {
|
fn test_mixed_floats_ints_chars_numeric_stable() {
|
||||||
test_helper("mixed_floats_ints_chars_numeric_stable", "-ns");
|
test_helper("mixed_floats_ints_chars_numeric_stable", &["-ns"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -347,12 +385,15 @@ fn test_numeric_floats2() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numeric_floats_with_nan2() {
|
fn test_numeric_floats_with_nan2() {
|
||||||
test_helper("numeric-floats-with-nan2", "-n");
|
test_helper(
|
||||||
|
"numeric-floats-with-nan2",
|
||||||
|
&["-n", "--numeric-sort", "--sort=numeric"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_human_block_sizes2() {
|
fn test_human_block_sizes2() {
|
||||||
for human_numeric_sort_param in vec!["-h", "--human-numeric-sort"] {
|
for human_numeric_sort_param in &["-h", "--human-numeric-sort", "--sort=human-numeric"] {
|
||||||
let input = "8981K\n909991M\n-8T\n21G\n0.8M";
|
let input = "8981K\n909991M\n-8T\n21G\n0.8M";
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.arg(human_numeric_sort_param)
|
.arg(human_numeric_sort_param)
|
||||||
|
@ -364,7 +405,7 @@ fn test_human_block_sizes2() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_month_default2() {
|
fn test_month_default2() {
|
||||||
for month_sort_param in vec!["-M", "--month-sort"] {
|
for month_sort_param in &["-M", "--month-sort", "--sort=month"] {
|
||||||
let input = "JAn\nMAY\n000may\nJun\nFeb";
|
let input = "JAn\nMAY\n000may\nJun\nFeb";
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.arg(month_sort_param)
|
.arg(month_sort_param)
|
||||||
|
@ -397,32 +438,32 @@ fn test_numeric_unique_ints2() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keys_open_ended() {
|
fn test_keys_open_ended() {
|
||||||
test_helper("keys_open_ended", "-k 2.3");
|
test_helper("keys_open_ended", &["-k 2.3"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keys_closed_range() {
|
fn test_keys_closed_range() {
|
||||||
test_helper("keys_closed_range", "-k 2.2,2.2");
|
test_helper("keys_closed_range", &["-k 2.2,2.2"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keys_multiple_ranges() {
|
fn test_keys_multiple_ranges() {
|
||||||
test_helper("keys_multiple_ranges", "-k 2,2 -k 3,3");
|
test_helper("keys_multiple_ranges", &["-k 2,2 -k 3,3"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keys_no_field_match() {
|
fn test_keys_no_field_match() {
|
||||||
test_helper("keys_no_field_match", "-k 4,4");
|
test_helper("keys_no_field_match", &["-k 4,4"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keys_no_char_match() {
|
fn test_keys_no_char_match() {
|
||||||
test_helper("keys_no_char_match", "-k 1.2");
|
test_helper("keys_no_char_match", &["-k 1.2"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_keys_custom_separator() {
|
fn test_keys_custom_separator() {
|
||||||
test_helper("keys_custom_separator", "-k 2.2,2.2 -t x");
|
test_helper("keys_custom_separator", &["-k 2.2,2.2 -t x"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -534,7 +575,7 @@ aaaa
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zero_terminated() {
|
fn test_zero_terminated() {
|
||||||
test_helper("zero-terminated", "-z");
|
test_helper("zero-terminated", &["-z"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -349,7 +349,6 @@ fn test_sleep_interval() {
|
||||||
new_ucmd!().arg("-s").arg("10").arg(FOOBAR_TXT).succeeds();
|
new_ucmd!().arg("-s").arg("10").arg(FOOBAR_TXT).succeeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Test for reading all but the first NUM bytes: `tail -c +3`.
|
/// Test for reading all but the first NUM bytes: `tail -c +3`.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_positive_bytes() {
|
fn test_positive_bytes() {
|
||||||
|
@ -360,7 +359,6 @@ fn test_positive_bytes() {
|
||||||
.stdout_is("cde");
|
.stdout_is("cde");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Test for reading all bytes, specified by `tail -c +0`.
|
/// Test for reading all bytes, specified by `tail -c +0`.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_positive_zero_bytes() {
|
fn test_positive_zero_bytes() {
|
||||||
|
@ -371,7 +369,6 @@ fn test_positive_zero_bytes() {
|
||||||
.stdout_is("abcde");
|
.stdout_is("abcde");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Test for reading all but the first NUM lines: `tail -n +3`.
|
/// Test for reading all but the first NUM lines: `tail -n +3`.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_positive_lines() {
|
fn test_positive_lines() {
|
||||||
|
@ -382,7 +379,6 @@ fn test_positive_lines() {
|
||||||
.stdout_is("c\nd\ne\n");
|
.stdout_is("c\nd\ne\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Test for reading all lines, specified by `tail -n +0`.
|
/// Test for reading all lines, specified by `tail -n +0`.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_positive_zero_lines() {
|
fn test_positive_zero_lines() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue