mirror of
https://github.com/uutils/coreutils
synced 2024-11-17 02:08:09 +00:00
Merge branch 'master' into ls/dereference-command-line
This commit is contained in:
commit
eec389fa94
106 changed files with 2942 additions and 1382 deletions
|
@ -22,6 +22,7 @@ search the issues to make sure no one else is working on it.
|
|||
1. Make sure that the code coverage is covering all of the cases, including errors.
|
||||
1. The code must be clippy-warning-free and rustfmt-compliant.
|
||||
1. Don't hesitate to move common functions into uucore if they can be reused by other binaries.
|
||||
1. Unsafe code should be documented with Safety comments.
|
||||
|
||||
## Commit messages
|
||||
|
||||
|
|
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -1362,6 +1362,12 @@ dependencies = [
|
|||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
@ -1388,9 +1394,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.68"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
|
||||
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
|
@ -1610,7 +1616,8 @@ name = "uu_cat"
|
|||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"quick-error",
|
||||
"nix 0.20.0",
|
||||
"thiserror",
|
||||
"unix_socket",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
@ -1816,7 +1823,7 @@ dependencies = [
|
|||
"quickcheck",
|
||||
"rand 0.7.3",
|
||||
"rand_chacha",
|
||||
"smallvec",
|
||||
"smallvec 0.6.14",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
|
@ -2285,10 +2292,11 @@ version = "0.0.6"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"fnv",
|
||||
"itertools 0.8.2",
|
||||
"itertools 0.10.0",
|
||||
"rand 0.7.3",
|
||||
"rayon",
|
||||
"semver",
|
||||
"smallvec 1.6.1",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
|
@ -2316,7 +2324,7 @@ dependencies = [
|
|||
name = "uu_stdbuf"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"clap",
|
||||
"tempfile",
|
||||
"uu_stdbuf_libstdbuf",
|
||||
"uucore",
|
||||
|
@ -2500,7 +2508,7 @@ dependencies = [
|
|||
name = "uu_unlink"
|
||||
version = "0.0.6"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
|
|
|
@ -111,6 +111,7 @@ fn basename(fullname: &str, suffix: &str) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::manual_strip)] // can be replaced with strip_suffix once the minimum rust version is 1.45
|
||||
fn strip_suffix(name: &str, suffix: &str) -> String {
|
||||
if name == suffix {
|
||||
return name.to_owned();
|
||||
|
|
|
@ -16,13 +16,16 @@ path = "src/cat.rs"
|
|||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
quick-error = "1.2.3"
|
||||
thiserror = "1.0"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
unix_socket = "0.5.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
|
||||
nix = "0.20"
|
||||
|
||||
[[bin]]
|
||||
name = "cat"
|
||||
path = "src/main.rs"
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
// (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||
// (c) Evgeniy Klyuchikov <evgeniy.klyuchikov@gmail.com>
|
||||
// (c) Joshua S. Miller <jsmiller@uchicago.edu>
|
||||
// (c) Árni Dagur <arni@dagur.eu>
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting
|
||||
|
||||
#[macro_use]
|
||||
extern crate quick_error;
|
||||
#[cfg(unix)]
|
||||
extern crate unix_socket;
|
||||
#[macro_use]
|
||||
|
@ -18,9 +17,9 @@ extern crate uucore;
|
|||
|
||||
// last synced with: cat (GNU coreutils) 8.13
|
||||
use clap::{App, Arg};
|
||||
use quick_error::ResultExt;
|
||||
use std::fs::{metadata, File};
|
||||
use std::io::{self, stderr, stdin, stdout, BufWriter, Read, Write};
|
||||
use std::io::{self, Read, Write};
|
||||
use thiserror::Error;
|
||||
use uucore::fs::is_stdin_interactive;
|
||||
|
||||
/// Unix domain socket support
|
||||
|
@ -31,12 +30,41 @@ use std::os::unix::fs::FileTypeExt;
|
|||
#[cfg(unix)]
|
||||
use unix_socket::UnixStream;
|
||||
|
||||
/// Linux splice support
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use nix::fcntl::{splice, SpliceFFlags};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use nix::unistd::pipe;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
static NAME: &str = "cat";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static SYNTAX: &str = "[OPTION]... [FILE]...";
|
||||
static SUMMARY: &str = "Concatenate FILE(s), or standard input, to standard output
|
||||
With no FILE, or when FILE is -, read standard input.";
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum CatError {
|
||||
/// Wrapper around `io::Error`
|
||||
#[error("{0}")]
|
||||
Io(#[from] io::Error),
|
||||
/// Wrapper around `nix::Error`
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[error("{0}")]
|
||||
Nix(#[from] nix::Error),
|
||||
/// Unknown file type; it's not a regular file, socket, etc.
|
||||
#[error("unknown filetype: {}", ft_debug)]
|
||||
UnknownFiletype {
|
||||
/// A debug print of the file type
|
||||
ft_debug: String,
|
||||
},
|
||||
#[error("Is a directory")]
|
||||
IsDirectory,
|
||||
}
|
||||
|
||||
type CatResult<T> = Result<T, CatError>;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum NumberingMode {
|
||||
None,
|
||||
|
@ -44,39 +72,6 @@ enum NumberingMode {
|
|||
All,
|
||||
}
|
||||
|
||||
quick_error! {
|
||||
#[derive(Debug)]
|
||||
enum CatError {
|
||||
/// Wrapper for io::Error with path context
|
||||
Input(err: io::Error, path: String) {
|
||||
display("cat: {0}: {1}", path, err)
|
||||
context(path: &'a str, err: io::Error) -> (err, path.to_owned())
|
||||
cause(err)
|
||||
}
|
||||
|
||||
/// Wrapper for io::Error with no context
|
||||
Output(err: io::Error) {
|
||||
display("cat: {0}", err) from()
|
||||
cause(err)
|
||||
}
|
||||
|
||||
/// Unknown Filetype classification
|
||||
UnknownFiletype(path: String) {
|
||||
display("cat: {0}: unknown filetype", path)
|
||||
}
|
||||
|
||||
/// At least one error was encountered in reading or writing
|
||||
EncounteredErrors(count: usize) {
|
||||
display("cat: encountered {0} errors", count)
|
||||
}
|
||||
|
||||
/// Denotes an error caused by trying to `cat` a directory
|
||||
IsDirectory(path: String) {
|
||||
display("cat: {0}: Is a directory", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct OutputOptions {
|
||||
/// Line numbering mode
|
||||
number: NumberingMode,
|
||||
|
@ -87,21 +82,56 @@ struct OutputOptions {
|
|||
/// display TAB characters as `tab`
|
||||
show_tabs: bool,
|
||||
|
||||
/// If `show_tabs == true`, this string will be printed in the
|
||||
/// place of tabs
|
||||
tab: String,
|
||||
|
||||
/// Can be set to show characters other than '\n' a the end of
|
||||
/// each line, e.g. $
|
||||
end_of_line: String,
|
||||
/// Show end of lines
|
||||
show_ends: bool,
|
||||
|
||||
/// use ^ and M- notation, except for LF (\\n) and TAB (\\t)
|
||||
show_nonprint: bool,
|
||||
}
|
||||
|
||||
impl OutputOptions {
|
||||
fn tab(&self) -> &'static str {
|
||||
if self.show_tabs {
|
||||
"^I"
|
||||
} else {
|
||||
"\t"
|
||||
}
|
||||
}
|
||||
|
||||
fn end_of_line(&self) -> &'static str {
|
||||
if self.show_ends {
|
||||
"$\n"
|
||||
} else {
|
||||
"\n"
|
||||
}
|
||||
}
|
||||
|
||||
/// We can write fast if we can simply copy the contents of the file to
|
||||
/// stdout, without augmenting the output with e.g. line numbers.
|
||||
fn can_write_fast(&self) -> bool {
|
||||
!(self.show_tabs
|
||||
|| self.show_nonprint
|
||||
|| self.show_ends
|
||||
|| self.squeeze_blank
|
||||
|| self.number != NumberingMode::None)
|
||||
}
|
||||
}
|
||||
|
||||
/// State that persists between output of each file. This struct is only used
|
||||
/// when we can't write fast.
|
||||
struct OutputState {
|
||||
/// The current line number
|
||||
line_number: usize,
|
||||
|
||||
/// Whether the output cursor is at the beginning of a new line
|
||||
at_line_start: bool,
|
||||
}
|
||||
|
||||
/// Represents an open file handle, stream, or other device
|
||||
struct InputHandle {
|
||||
reader: Box<dyn Read>,
|
||||
struct InputHandle<R: Read> {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
file_descriptor: RawFd,
|
||||
reader: R,
|
||||
is_interactive: bool,
|
||||
}
|
||||
|
||||
|
@ -124,8 +154,6 @@ enum InputType {
|
|||
Socket,
|
||||
}
|
||||
|
||||
type CatResult<T> = Result<T, CatError>;
|
||||
|
||||
mod options {
|
||||
pub static FILE: &str = "file";
|
||||
pub static SHOW_ALL: &str = "show-all";
|
||||
|
@ -243,30 +271,14 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
None => vec!["-".to_owned()],
|
||||
};
|
||||
|
||||
let can_write_fast = !(show_tabs
|
||||
|| show_nonprint
|
||||
|| show_ends
|
||||
|| squeeze_blank
|
||||
|| number_mode != NumberingMode::None);
|
||||
|
||||
let success = if can_write_fast {
|
||||
write_fast(files).is_ok()
|
||||
} else {
|
||||
let tab = if show_tabs { "^I" } else { "\t" }.to_owned();
|
||||
|
||||
let end_of_line = if show_ends { "$\n" } else { "\n" }.to_owned();
|
||||
|
||||
let options = OutputOptions {
|
||||
end_of_line,
|
||||
number: number_mode,
|
||||
show_nonprint,
|
||||
show_tabs,
|
||||
squeeze_blank,
|
||||
tab,
|
||||
};
|
||||
|
||||
write_lines(files, &options).is_ok()
|
||||
let options = OutputOptions {
|
||||
show_ends,
|
||||
number: number_mode,
|
||||
show_nonprint,
|
||||
show_tabs,
|
||||
squeeze_blank,
|
||||
};
|
||||
let success = cat_files(files, &options).is_ok();
|
||||
|
||||
if success {
|
||||
0
|
||||
|
@ -275,6 +287,76 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
fn cat_handle<R: Read>(
|
||||
handle: &mut InputHandle<R>,
|
||||
options: &OutputOptions,
|
||||
state: &mut OutputState,
|
||||
) -> CatResult<()> {
|
||||
if options.can_write_fast() {
|
||||
write_fast(handle)
|
||||
} else {
|
||||
write_lines(handle, &options, state)
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
|
||||
if path == "-" {
|
||||
let stdin = io::stdin();
|
||||
let mut handle = InputHandle {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
file_descriptor: stdin.as_raw_fd(),
|
||||
reader: stdin,
|
||||
is_interactive: is_stdin_interactive(),
|
||||
};
|
||||
return cat_handle(&mut handle, &options, state);
|
||||
}
|
||||
match get_input_type(path)? {
|
||||
InputType::Directory => Err(CatError::IsDirectory),
|
||||
#[cfg(unix)]
|
||||
InputType::Socket => {
|
||||
let socket = UnixStream::connect(path)?;
|
||||
socket.shutdown(Shutdown::Write)?;
|
||||
let mut handle = InputHandle {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
file_descriptor: socket.as_raw_fd(),
|
||||
reader: socket,
|
||||
is_interactive: false,
|
||||
};
|
||||
cat_handle(&mut handle, &options, state)
|
||||
}
|
||||
_ => {
|
||||
let file = File::open(path)?;
|
||||
let mut handle = InputHandle {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
file_descriptor: file.as_raw_fd(),
|
||||
reader: file,
|
||||
is_interactive: false,
|
||||
};
|
||||
cat_handle(&mut handle, &options, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_files(files: Vec<String>, options: &OutputOptions) -> Result<(), u32> {
|
||||
let mut error_count = 0;
|
||||
let mut state = OutputState {
|
||||
line_number: 1,
|
||||
at_line_start: true,
|
||||
};
|
||||
|
||||
for path in &files {
|
||||
if let Err(err) = cat_path(path, &options, &mut state) {
|
||||
show_info!("{}: {}", path, err);
|
||||
error_count += 1;
|
||||
}
|
||||
}
|
||||
if error_count == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error_count)
|
||||
}
|
||||
}
|
||||
|
||||
/// Classifies the `InputType` of file at `path` if possible
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -285,7 +367,8 @@ fn get_input_type(path: &str) -> CatResult<InputType> {
|
|||
return Ok(InputType::StdIn);
|
||||
}
|
||||
|
||||
match metadata(path).context(path)?.file_type() {
|
||||
let ft = metadata(path)?.file_type();
|
||||
match ft {
|
||||
#[cfg(unix)]
|
||||
ft if ft.is_block_device() => Ok(InputType::BlockDevice),
|
||||
#[cfg(unix)]
|
||||
|
@ -297,125 +380,116 @@ fn get_input_type(path: &str) -> CatResult<InputType> {
|
|||
ft if ft.is_dir() => Ok(InputType::Directory),
|
||||
ft if ft.is_file() => Ok(InputType::File),
|
||||
ft if ft.is_symlink() => Ok(InputType::SymLink),
|
||||
_ => Err(CatError::UnknownFiletype(path.to_owned())),
|
||||
_ => Err(CatError::UnknownFiletype {
|
||||
ft_debug: format!("{:?}", ft),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an InputHandle from which a Reader can be accessed or an
|
||||
/// error
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - `InputHandler` will wrap a reader from this file path
|
||||
fn open(path: &str) -> CatResult<InputHandle> {
|
||||
if path == "-" {
|
||||
let stdin = stdin();
|
||||
return Ok(InputHandle {
|
||||
reader: Box::new(stdin) as Box<dyn Read>,
|
||||
is_interactive: is_stdin_interactive(),
|
||||
});
|
||||
}
|
||||
|
||||
match get_input_type(path)? {
|
||||
InputType::Directory => Err(CatError::IsDirectory(path.to_owned())),
|
||||
#[cfg(unix)]
|
||||
InputType::Socket => {
|
||||
let socket = UnixStream::connect(path).context(path)?;
|
||||
socket.shutdown(Shutdown::Write).context(path)?;
|
||||
Ok(InputHandle {
|
||||
reader: Box::new(socket) as Box<dyn Read>,
|
||||
is_interactive: false,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
let file = File::open(path).context(path)?;
|
||||
Ok(InputHandle {
|
||||
reader: Box::new(file) as Box<dyn Read>,
|
||||
is_interactive: false,
|
||||
})
|
||||
/// Writes handle to stdout with no configuration. This allows a
|
||||
/// simple memory copy.
|
||||
fn write_fast<R: Read>(handle: &mut InputHandle<R>) -> CatResult<()> {
|
||||
let stdout = io::stdout();
|
||||
let mut stdout_lock = stdout.lock();
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
// If we're on Linux or Android, try to use the splice() system call
|
||||
// for faster writing. If it works, we're done.
|
||||
if !write_fast_using_splice(handle, stdout_lock.as_raw_fd())? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
// If we're not on Linux or Android, or the splice() call failed,
|
||||
// fall back on slower writing.
|
||||
let mut buf = [0; 1024 * 64];
|
||||
while let Ok(n) = handle.reader.read(&mut buf) {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
stdout_lock.write_all(&buf[..n])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes files to stdout with no configuration. This allows a
|
||||
/// simple memory copy. Returns `Ok(())` if no errors were
|
||||
/// encountered, or an error with the number of errors encountered.
|
||||
/// This function is called from `write_fast()` on Linux and Android. The
|
||||
/// function `splice()` is used to move data between two file descriptors
|
||||
/// without copying between kernel- and userspace. This results in a large
|
||||
/// speedup.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `files` - There is no short circuit when encountering an error
|
||||
/// reading a file in this vector
|
||||
fn write_fast(files: Vec<String>) -> CatResult<()> {
|
||||
let mut writer = stdout();
|
||||
let mut in_buf = [0; 1024 * 64];
|
||||
let mut error_count = 0;
|
||||
/// The `bool` in the result value indicates if we need to fall back to normal
|
||||
/// copying or not. False means we don't have to.
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[inline]
|
||||
fn write_fast_using_splice<R: Read>(handle: &mut InputHandle<R>, writer: RawFd) -> CatResult<bool> {
|
||||
const BUF_SIZE: usize = 1024 * 16;
|
||||
|
||||
for file in files {
|
||||
match open(&file[..]) {
|
||||
Ok(mut handle) => {
|
||||
while let Ok(n) = handle.reader.read(&mut in_buf) {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
writer.write_all(&in_buf[..n]).context(&file[..])?;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
writeln!(&mut stderr(), "{}", error)?;
|
||||
error_count += 1;
|
||||
let (pipe_rd, pipe_wr) = pipe()?;
|
||||
|
||||
// We only fall back if splice fails on the first call.
|
||||
match splice(
|
||||
handle.file_descriptor,
|
||||
None,
|
||||
pipe_wr,
|
||||
None,
|
||||
BUF_SIZE,
|
||||
SpliceFFlags::empty(),
|
||||
) {
|
||||
Ok(n) => {
|
||||
if n == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
splice_exact(pipe_rd, writer, n)?;
|
||||
}
|
||||
Err(_) => {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
match error_count {
|
||||
0 => Ok(()),
|
||||
_ => Err(CatError::EncounteredErrors(error_count)),
|
||||
loop {
|
||||
let n = splice(
|
||||
handle.file_descriptor,
|
||||
None,
|
||||
pipe_wr,
|
||||
None,
|
||||
BUF_SIZE,
|
||||
SpliceFFlags::empty(),
|
||||
)?;
|
||||
if n == 0 {
|
||||
// We read 0 bytes from the input,
|
||||
// which means we're done copying.
|
||||
break;
|
||||
}
|
||||
splice_exact(pipe_rd, writer, n)?;
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// State that persists between output of each file
|
||||
struct OutputState {
|
||||
/// The current line number
|
||||
line_number: usize,
|
||||
|
||||
/// Whether the output cursor is at the beginning of a new line
|
||||
at_line_start: bool,
|
||||
}
|
||||
|
||||
/// Writes files to stdout with `options` as configuration. Returns
|
||||
/// `Ok(())` if no errors were encountered, or an error with the
|
||||
/// number of errors encountered.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `files` - There is no short circuit when encountering an error
|
||||
/// reading a file in this vector
|
||||
fn write_lines(files: Vec<String>, options: &OutputOptions) -> CatResult<()> {
|
||||
let mut error_count = 0;
|
||||
let mut state = OutputState {
|
||||
line_number: 1,
|
||||
at_line_start: true,
|
||||
};
|
||||
|
||||
for file in files {
|
||||
if let Err(error) = write_file_lines(&file, options, &mut state) {
|
||||
writeln!(&mut stderr(), "{}", error).context(&file[..])?;
|
||||
error_count += 1;
|
||||
/// Splice wrapper which handles short writes
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[inline]
|
||||
fn splice_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> {
|
||||
let mut left = num_bytes;
|
||||
loop {
|
||||
let written = splice(read_fd, None, write_fd, None, left, SpliceFFlags::empty())?;
|
||||
left -= written;
|
||||
if left == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match error_count {
|
||||
0 => Ok(()),
|
||||
_ => Err(CatError::EncounteredErrors(error_count)),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Outputs file contents to stdout in a line-by-line fashion,
|
||||
/// propagating any errors that might occur.
|
||||
fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
|
||||
let mut handle = open(file)?;
|
||||
fn write_lines<R: Read>(
|
||||
handle: &mut InputHandle<R>,
|
||||
options: &OutputOptions,
|
||||
state: &mut OutputState,
|
||||
) -> CatResult<()> {
|
||||
let mut in_buf = [0; 1024 * 31];
|
||||
let mut writer = BufWriter::with_capacity(1024 * 64, stdout());
|
||||
let stdout = io::stdout();
|
||||
let mut writer = stdout.lock();
|
||||
let mut one_blank_kept = false;
|
||||
|
||||
while let Ok(n) = handle.reader.read(&mut in_buf) {
|
||||
|
@ -433,9 +507,9 @@ fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState
|
|||
write!(&mut writer, "{0:6}\t", state.line_number)?;
|
||||
state.line_number += 1;
|
||||
}
|
||||
writer.write_all(options.end_of_line.as_bytes())?;
|
||||
writer.write_all(options.end_of_line().as_bytes())?;
|
||||
if handle.is_interactive {
|
||||
writer.flush().context(file)?;
|
||||
writer.flush()?;
|
||||
}
|
||||
}
|
||||
state.at_line_start = true;
|
||||
|
@ -450,7 +524,7 @@ fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState
|
|||
|
||||
// print to end of line or end of buffer
|
||||
let offset = if options.show_nonprint {
|
||||
write_nonprint_to_end(&in_buf[pos..], &mut writer, options.tab.as_bytes())
|
||||
write_nonprint_to_end(&in_buf[pos..], &mut writer, options.tab().as_bytes())
|
||||
} else if options.show_tabs {
|
||||
write_tab_to_end(&in_buf[pos..], &mut writer)
|
||||
} else {
|
||||
|
@ -462,7 +536,7 @@ fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState
|
|||
break;
|
||||
}
|
||||
// print suitable end of line
|
||||
writer.write_all(options.end_of_line.as_bytes())?;
|
||||
writer.write_all(options.end_of_line().as_bytes())?;
|
||||
if handle.is_interactive {
|
||||
writer.flush()?;
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ impl Chgrper {
|
|||
|
||||
ret = match wrap_chgrp(path, &meta, self.dest_gid, follow, self.verbosity.clone()) {
|
||||
Ok(n) => {
|
||||
if n != "" {
|
||||
if !n.is_empty() {
|
||||
show_info!("{}", n);
|
||||
}
|
||||
0
|
||||
|
|
|
@ -171,13 +171,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
// of a prefix '-' if it's associated with MODE
|
||||
// e.g. "chmod -v -xw -R FILE" -> "chmod -v xw -R FILE"
|
||||
pub fn strip_minus_from_mode(args: &mut Vec<String>) -> bool {
|
||||
for i in 0..args.len() {
|
||||
if args[i].starts_with("-") {
|
||||
if let Some(second) = args[i].chars().nth(1) {
|
||||
for arg in args {
|
||||
if arg.starts_with('-') {
|
||||
if let Some(second) = arg.chars().nth(1) {
|
||||
match second {
|
||||
'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'..='7' => {
|
||||
// TODO: use strip_prefix() once minimum rust version reaches 1.45.0
|
||||
args[i] = args[i][1..args[i].len()].to_string();
|
||||
*arg = arg[1..arg.len()].to_string();
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -391,7 +391,7 @@ impl Chowner {
|
|||
self.verbosity.clone(),
|
||||
) {
|
||||
Ok(n) => {
|
||||
if n != "" {
|
||||
if !n.is_empty() {
|
||||
show_info!("{}", n);
|
||||
}
|
||||
0
|
||||
|
@ -446,7 +446,7 @@ impl Chowner {
|
|||
self.verbosity.clone(),
|
||||
) {
|
||||
Ok(n) => {
|
||||
if n != "" {
|
||||
if !n.is_empty() {
|
||||
show_info!("{}", n);
|
||||
}
|
||||
0
|
||||
|
|
|
@ -104,7 +104,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
_ => {
|
||||
let mut vector: Vec<&str> = Vec::new();
|
||||
for (&k, v) in matches.args.iter() {
|
||||
vector.push(k.clone());
|
||||
vector.push(k);
|
||||
vector.push(&v.vals[0].to_str().unwrap());
|
||||
}
|
||||
vector
|
||||
|
@ -133,7 +133,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
|
|||
let userspec = match userspec_str {
|
||||
Some(ref u) => {
|
||||
let s: Vec<&str> = u.split(':').collect();
|
||||
if s.len() != 2 || s.iter().any(|&spec| spec == "") {
|
||||
if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
|
||||
crash!(1, "invalid userspec: `{}`", u)
|
||||
};
|
||||
s
|
||||
|
@ -142,16 +142,16 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
|
|||
};
|
||||
|
||||
let (user, group) = if userspec.is_empty() {
|
||||
(&user_str[..], &group_str[..])
|
||||
(user_str, group_str)
|
||||
} else {
|
||||
(&userspec[0][..], &userspec[1][..])
|
||||
(userspec[0], userspec[1])
|
||||
};
|
||||
|
||||
enter_chroot(root);
|
||||
|
||||
set_groups_from_str(&groups_str[..]);
|
||||
set_main_group(&group[..]);
|
||||
set_user(&user[..]);
|
||||
set_groups_from_str(groups_str);
|
||||
set_main_group(group);
|
||||
set_user(user);
|
||||
}
|
||||
|
||||
fn enter_chroot(root: &Path) {
|
||||
|
|
|
@ -132,7 +132,9 @@ macro_rules! prompt_yes(
|
|||
|
||||
pub type CopyResult<T> = Result<T, Error>;
|
||||
pub type Source = PathBuf;
|
||||
pub type SourceSlice = Path;
|
||||
pub type Target = PathBuf;
|
||||
pub type TargetSlice = Path;
|
||||
|
||||
/// Specifies whether when overwrite files
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
|
@ -547,14 +549,13 @@ impl FromStr for Attribute {
|
|||
}
|
||||
|
||||
fn add_all_attributes() -> Vec<Attribute> {
|
||||
let mut attr = Vec::new();
|
||||
use Attribute::*;
|
||||
|
||||
let mut attr = vec![Ownership, Timestamps, Context, Xattr, Links];
|
||||
|
||||
#[cfg(unix)]
|
||||
attr.push(Attribute::Mode);
|
||||
attr.push(Attribute::Ownership);
|
||||
attr.push(Attribute::Timestamps);
|
||||
attr.push(Attribute::Context);
|
||||
attr.push(Attribute::Xattr);
|
||||
attr.push(Attribute::Links);
|
||||
attr.insert(0, Mode);
|
||||
|
||||
attr
|
||||
}
|
||||
|
||||
|
@ -665,7 +666,7 @@ impl TargetType {
|
|||
///
|
||||
/// Treat target as a dir if we have multiple sources or the target
|
||||
/// exists and already is a directory
|
||||
fn determine(sources: &[Source], target: &Target) -> TargetType {
|
||||
fn determine(sources: &[Source], target: &TargetSlice) -> TargetType {
|
||||
if sources.len() > 1 || target.is_dir() {
|
||||
TargetType::Directory
|
||||
} else {
|
||||
|
@ -714,7 +715,7 @@ fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<S
|
|||
|
||||
fn preserve_hardlinks(
|
||||
hard_links: &mut Vec<(String, u64)>,
|
||||
source: &std::path::PathBuf,
|
||||
source: &std::path::Path,
|
||||
dest: std::path::PathBuf,
|
||||
found_hard_link: &mut bool,
|
||||
) -> CopyResult<()> {
|
||||
|
@ -788,7 +789,7 @@ fn preserve_hardlinks(
|
|||
/// Behavior depends on `options`, see [`Options`] for details.
|
||||
///
|
||||
/// [`Options`]: ./struct.Options.html
|
||||
fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()> {
|
||||
fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResult<()> {
|
||||
let target_type = TargetType::determine(sources, target);
|
||||
verify_target_type(target, &target_type)?;
|
||||
|
||||
|
@ -840,7 +841,7 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()
|
|||
|
||||
fn construct_dest_path(
|
||||
source_path: &Path,
|
||||
target: &Target,
|
||||
target: &TargetSlice,
|
||||
target_type: &TargetType,
|
||||
options: &Options,
|
||||
) -> CopyResult<PathBuf> {
|
||||
|
@ -870,8 +871,8 @@ fn construct_dest_path(
|
|||
}
|
||||
|
||||
fn copy_source(
|
||||
source: &Source,
|
||||
target: &Target,
|
||||
source: &SourceSlice,
|
||||
target: &TargetSlice,
|
||||
target_type: &TargetType,
|
||||
options: &Options,
|
||||
) -> CopyResult<()> {
|
||||
|
@ -912,7 +913,7 @@ fn adjust_canonicalization(p: &Path) -> Cow<Path> {
|
|||
///
|
||||
/// Any errors encountered copying files in the tree will be logged but
|
||||
/// will not cause a short-circuit.
|
||||
fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult<()> {
|
||||
fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyResult<()> {
|
||||
if !options.recursive {
|
||||
return Err(format!("omitting directory '{}'", root.display()).into());
|
||||
}
|
||||
|
@ -1068,6 +1069,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
|
|||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[allow(clippy::unnecessary_wraps)] // needed for windows version
|
||||
fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> {
|
||||
match std::os::unix::fs::symlink(source, dest).context(context) {
|
||||
Ok(_) => Ok(()),
|
||||
|
|
|
@ -406,7 +406,7 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
|
|||
continue;
|
||||
}
|
||||
|
||||
if !path.metadata().is_ok() {
|
||||
if path.metadata().is_err() {
|
||||
show_error!("{}: No such file or directory", filename);
|
||||
continue;
|
||||
}
|
||||
|
@ -487,7 +487,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.help("filter field columns from the input source")
|
||||
.takes_value(true)
|
||||
.allow_hyphen_values(true)
|
||||
.value_name("LIST")
|
||||
.value_name("LIST")
|
||||
.display_order(4),
|
||||
)
|
||||
.arg(
|
||||
|
@ -535,40 +535,36 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
matches.value_of(options::CHARACTERS),
|
||||
matches.value_of(options::FIELDS),
|
||||
) {
|
||||
(Some(byte_ranges), None, None) => {
|
||||
list_to_ranges(&byte_ranges[..], complement).map(|ranges| {
|
||||
Mode::Bytes(
|
||||
ranges,
|
||||
Options {
|
||||
out_delim: Some(
|
||||
matches
|
||||
.value_of(options::OUTPUT_DELIMITER)
|
||||
.unwrap_or_default()
|
||||
.to_owned(),
|
||||
),
|
||||
zero_terminated: matches.is_present(options::ZERO_TERMINATED),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
(None, Some(char_ranges), None) => {
|
||||
list_to_ranges(&char_ranges[..], complement).map(|ranges| {
|
||||
Mode::Characters(
|
||||
ranges,
|
||||
Options {
|
||||
out_delim: Some(
|
||||
matches
|
||||
.value_of(options::OUTPUT_DELIMITER)
|
||||
.unwrap_or_default()
|
||||
.to_owned(),
|
||||
),
|
||||
zero_terminated: matches.is_present(options::ZERO_TERMINATED),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
(Some(byte_ranges), None, None) => list_to_ranges(byte_ranges, complement).map(|ranges| {
|
||||
Mode::Bytes(
|
||||
ranges,
|
||||
Options {
|
||||
out_delim: Some(
|
||||
matches
|
||||
.value_of(options::OUTPUT_DELIMITER)
|
||||
.unwrap_or_default()
|
||||
.to_owned(),
|
||||
),
|
||||
zero_terminated: matches.is_present(options::ZERO_TERMINATED),
|
||||
},
|
||||
)
|
||||
}),
|
||||
(None, Some(char_ranges), None) => list_to_ranges(char_ranges, complement).map(|ranges| {
|
||||
Mode::Characters(
|
||||
ranges,
|
||||
Options {
|
||||
out_delim: Some(
|
||||
matches
|
||||
.value_of(options::OUTPUT_DELIMITER)
|
||||
.unwrap_or_default()
|
||||
.to_owned(),
|
||||
),
|
||||
zero_terminated: matches.is_present(options::ZERO_TERMINATED),
|
||||
},
|
||||
)
|
||||
}),
|
||||
(None, None, Some(field_ranges)) => {
|
||||
list_to_ranges(&field_ranges[..], complement).and_then(|ranges| {
|
||||
list_to_ranges(field_ranges, complement).and_then(|ranges| {
|
||||
let out_delim = match matches.value_of(options::OUTPUT_DELIMITER) {
|
||||
Some(s) => {
|
||||
if s.is_empty() {
|
||||
|
|
|
@ -116,7 +116,6 @@ struct Options {
|
|||
show_listed_fs: bool,
|
||||
show_fs_type: bool,
|
||||
show_inode_instead: bool,
|
||||
print_grand_total: bool,
|
||||
// block_size: usize,
|
||||
human_readable_base: i64,
|
||||
fs_selector: FsSelector,
|
||||
|
@ -286,7 +285,6 @@ impl Options {
|
|||
show_listed_fs: false,
|
||||
show_fs_type: false,
|
||||
show_inode_instead: false,
|
||||
print_grand_total: false,
|
||||
// block_size: match env::var("BLOCKSIZE") {
|
||||
// Ok(size) => size.parse().unwrap(),
|
||||
// Err(_) => 512,
|
||||
|
@ -871,9 +869,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
if matches.is_present(OPT_ALL) {
|
||||
opt.show_all_fs = true;
|
||||
}
|
||||
if matches.is_present(OPT_TOTAL) {
|
||||
opt.print_grand_total = true;
|
||||
}
|
||||
if matches.is_present(OPT_INODES) {
|
||||
opt.show_inode_instead = true;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use chrono::Local;
|
|||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{stderr, Result, Write};
|
||||
use std::io::{stderr, ErrorKind, Result, Write};
|
||||
use std::iter;
|
||||
#[cfg(not(windows))]
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
@ -296,7 +296,21 @@ fn du(
|
|||
}
|
||||
}
|
||||
}
|
||||
Err(error) => show_error!("{}", error),
|
||||
Err(error) => match error.kind() {
|
||||
ErrorKind::PermissionDenied => {
|
||||
let description = format!(
|
||||
"cannot access '{}'",
|
||||
entry
|
||||
.path()
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.unwrap_or("<Un-printable path>")
|
||||
);
|
||||
let error_message = "Permission denied";
|
||||
show_error_custom_description!(description, "{}", error_message)
|
||||
}
|
||||
_ => show_error!("{}", error),
|
||||
},
|
||||
},
|
||||
Err(error) => show_error!("{}", error),
|
||||
}
|
||||
|
@ -322,7 +336,7 @@ fn convert_size_human(size: u64, multiplier: u64, _block_size: u64) -> String {
|
|||
}
|
||||
}
|
||||
if size == 0 {
|
||||
return format!("0");
|
||||
return "0".to_string();
|
||||
}
|
||||
format!("{}B", size)
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ fn print_expr_error(expr_error: &str) -> ! {
|
|||
crash!(2, "{}", expr_error)
|
||||
}
|
||||
|
||||
fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::ASTNode>, String>) -> Result<String, String> {
|
||||
fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::AstNode>, String>) -> Result<String, String> {
|
||||
if maybe_ast.is_err() {
|
||||
Err(maybe_ast.err().unwrap())
|
||||
} else {
|
||||
|
|
|
@ -17,10 +17,10 @@ use onig::{Regex, RegexOptions, Syntax};
|
|||
use crate::tokens::Token;
|
||||
|
||||
type TokenStack = Vec<(usize, Token)>;
|
||||
pub type OperandsList = Vec<Box<ASTNode>>;
|
||||
pub type OperandsList = Vec<Box<AstNode>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ASTNode {
|
||||
pub enum AstNode {
|
||||
Leaf {
|
||||
token_idx: usize,
|
||||
value: String,
|
||||
|
@ -31,7 +31,7 @@ pub enum ASTNode {
|
|||
operands: OperandsList,
|
||||
},
|
||||
}
|
||||
impl ASTNode {
|
||||
impl AstNode {
|
||||
fn debug_dump(&self) {
|
||||
self.debug_dump_impl(1);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl ASTNode {
|
|||
print!("\t",);
|
||||
}
|
||||
match *self {
|
||||
ASTNode::Leaf {
|
||||
AstNode::Leaf {
|
||||
ref token_idx,
|
||||
ref value,
|
||||
} => println!(
|
||||
|
@ -49,7 +49,7 @@ impl ASTNode {
|
|||
token_idx,
|
||||
self.evaluate()
|
||||
),
|
||||
ASTNode::Node {
|
||||
AstNode::Node {
|
||||
ref token_idx,
|
||||
ref op_type,
|
||||
ref operands,
|
||||
|
@ -67,23 +67,23 @@ impl ASTNode {
|
|||
}
|
||||
}
|
||||
|
||||
fn new_node(token_idx: usize, op_type: &str, operands: OperandsList) -> Box<ASTNode> {
|
||||
Box::new(ASTNode::Node {
|
||||
fn new_node(token_idx: usize, op_type: &str, operands: OperandsList) -> Box<AstNode> {
|
||||
Box::new(AstNode::Node {
|
||||
token_idx,
|
||||
op_type: op_type.into(),
|
||||
operands,
|
||||
})
|
||||
}
|
||||
fn new_leaf(token_idx: usize, value: &str) -> Box<ASTNode> {
|
||||
Box::new(ASTNode::Leaf {
|
||||
fn new_leaf(token_idx: usize, value: &str) -> Box<AstNode> {
|
||||
Box::new(AstNode::Leaf {
|
||||
token_idx,
|
||||
value: value.into(),
|
||||
})
|
||||
}
|
||||
pub fn evaluate(&self) -> Result<String, String> {
|
||||
match *self {
|
||||
ASTNode::Leaf { ref value, .. } => Ok(value.clone()),
|
||||
ASTNode::Node { ref op_type, .. } => match self.operand_values() {
|
||||
AstNode::Leaf { ref value, .. } => Ok(value.clone()),
|
||||
AstNode::Node { ref op_type, .. } => match self.operand_values() {
|
||||
Err(reason) => Err(reason),
|
||||
Ok(operand_values) => match op_type.as_ref() {
|
||||
"+" => infix_operator_two_ints(
|
||||
|
@ -161,7 +161,7 @@ impl ASTNode {
|
|||
}
|
||||
}
|
||||
pub fn operand_values(&self) -> Result<Vec<String>, String> {
|
||||
if let ASTNode::Node { ref operands, .. } = *self {
|
||||
if let AstNode::Node { ref operands, .. } = *self {
|
||||
let mut out = Vec::with_capacity(operands.len());
|
||||
for operand in operands {
|
||||
match operand.evaluate() {
|
||||
|
@ -178,7 +178,7 @@ impl ASTNode {
|
|||
|
||||
pub fn tokens_to_ast(
|
||||
maybe_tokens: Result<Vec<(usize, Token)>, String>,
|
||||
) -> Result<Box<ASTNode>, String> {
|
||||
) -> Result<Box<AstNode>, String> {
|
||||
if maybe_tokens.is_err() {
|
||||
Err(maybe_tokens.err().unwrap())
|
||||
} else {
|
||||
|
@ -212,7 +212,7 @@ pub fn tokens_to_ast(
|
|||
}
|
||||
}
|
||||
|
||||
fn maybe_dump_ast(result: &Result<Box<ASTNode>, String>) {
|
||||
fn maybe_dump_ast(result: &Result<Box<AstNode>, String>) {
|
||||
use std::env;
|
||||
if let Ok(debug_var) = env::var("EXPR_DEBUG_AST") {
|
||||
if debug_var == "1" {
|
||||
|
@ -238,11 +238,11 @@ fn maybe_dump_rpn(rpn: &TokenStack) {
|
|||
}
|
||||
}
|
||||
|
||||
fn ast_from_rpn(rpn: &mut TokenStack) -> Result<Box<ASTNode>, String> {
|
||||
fn ast_from_rpn(rpn: &mut TokenStack) -> Result<Box<AstNode>, String> {
|
||||
match rpn.pop() {
|
||||
None => Err("syntax error (premature end of expression)".to_owned()),
|
||||
|
||||
Some((token_idx, Token::Value { value })) => Ok(ASTNode::new_leaf(token_idx, &value)),
|
||||
Some((token_idx, Token::Value { value })) => Ok(AstNode::new_leaf(token_idx, &value)),
|
||||
|
||||
Some((token_idx, Token::InfixOp { value, .. })) => {
|
||||
maybe_ast_node(token_idx, &value, 2, rpn)
|
||||
|
@ -262,7 +262,7 @@ fn maybe_ast_node(
|
|||
op_type: &str,
|
||||
arity: usize,
|
||||
rpn: &mut TokenStack,
|
||||
) -> Result<Box<ASTNode>, String> {
|
||||
) -> Result<Box<AstNode>, String> {
|
||||
let mut operands = Vec::with_capacity(arity);
|
||||
for _ in 0..arity {
|
||||
match ast_from_rpn(rpn) {
|
||||
|
@ -271,7 +271,7 @@ fn maybe_ast_node(
|
|||
}
|
||||
}
|
||||
operands.reverse();
|
||||
Ok(ASTNode::new_node(token_idx, op_type, operands))
|
||||
Ok(AstNode::new_node(token_idx, op_type, operands))
|
||||
}
|
||||
|
||||
fn move_rest_of_ops_to_out(
|
||||
|
|
|
@ -267,7 +267,7 @@ impl<'a> ParagraphStream<'a> {
|
|||
#[allow(clippy::match_like_matches_macro)]
|
||||
// `matches!(...)` macro not stabilized until rust v1.42
|
||||
l_slice[..colon_posn].chars().all(|x| match x as usize {
|
||||
y if y < 33 || y > 126 => false,
|
||||
y if !(33..=126).contains(&y) => false,
|
||||
_ => true,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::with_name(options::FILE).hidden(true).multiple(true))
|
||||
.get_matches_from(args.clone());
|
||||
.get_matches_from(args);
|
||||
|
||||
let bytes = matches.is_present(options::BYTES);
|
||||
let spaces = matches.is_present(options::SPACES);
|
||||
|
|
|
@ -78,7 +78,7 @@ fn detect_algo<'a>(
|
|||
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
|
||||
"b2sum" => ("BLAKE2", Box::new(Blake2b::new(64)) as Box<dyn Digest>, 512),
|
||||
"sha3sum" => match matches.value_of("bits") {
|
||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Ok(224) => (
|
||||
"SHA3-224",
|
||||
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
||||
|
@ -128,7 +128,7 @@ fn detect_algo<'a>(
|
|||
512,
|
||||
),
|
||||
"shake128sum" => match matches.value_of("bits") {
|
||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Ok(bits) => (
|
||||
"SHAKE128",
|
||||
Box::new(Shake128::new()) as Box<dyn Digest>,
|
||||
|
@ -139,7 +139,7 @@ fn detect_algo<'a>(
|
|||
None => crash!(1, "--bits required for SHAKE-128"),
|
||||
},
|
||||
"shake256sum" => match matches.value_of("bits") {
|
||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Ok(bits) => (
|
||||
"SHAKE256",
|
||||
Box::new(Shake256::new()) as Box<dyn Digest>,
|
||||
|
@ -182,7 +182,7 @@ fn detect_algo<'a>(
|
|||
}
|
||||
if matches.is_present("sha3") {
|
||||
match matches.value_of("bits") {
|
||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Ok(224) => set_or_crash(
|
||||
"SHA3-224",
|
||||
Box::new(Sha3_224::new()) as Box<dyn Digest>,
|
||||
|
@ -226,7 +226,7 @@ fn detect_algo<'a>(
|
|||
}
|
||||
if matches.is_present("shake128") {
|
||||
match matches.value_of("bits") {
|
||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Ok(bits) => set_or_crash("SHAKE128", Box::new(Shake128::new()), bits),
|
||||
Err(err) => crash!(1, "{}", err),
|
||||
},
|
||||
|
@ -235,7 +235,7 @@ fn detect_algo<'a>(
|
|||
}
|
||||
if matches.is_present("shake256") {
|
||||
match matches.value_of("bits") {
|
||||
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
|
||||
Some(bits_str) => match (&bits_str).parse::<usize>() {
|
||||
Ok(bits) => set_or_crash("SHAKE256", Box::new(Shake256::new()), bits),
|
||||
Err(err) => crash!(1, "{}", err),
|
||||
},
|
||||
|
@ -253,7 +253,7 @@ fn detect_algo<'a>(
|
|||
|
||||
// TODO: return custom error type
|
||||
fn parse_bit_num(arg: &str) -> Result<usize, ParseIntError> {
|
||||
usize::from_str_radix(arg, 10)
|
||||
arg.parse()
|
||||
}
|
||||
|
||||
fn is_valid_bit_num(arg: String) -> Result<(), String> {
|
||||
|
|
|
@ -625,7 +625,7 @@ mod tests {
|
|||
assert_eq!(arg_outputs("head"), Ok("head".to_owned()));
|
||||
}
|
||||
#[test]
|
||||
#[cfg(linux)]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_arg_iterate_bad_encoding() {
|
||||
let invalid = unsafe { std::str::from_utf8_unchecked(b"\x80\x81") };
|
||||
// this arises from a conversion from OsString to &str
|
||||
|
|
|
@ -23,9 +23,11 @@ use std::fs;
|
|||
use std::fs::File;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::result::Result;
|
||||
|
||||
const DEFAULT_MODE: u32 = 0o755;
|
||||
const DEFAULT_STRIP_PROGRAM: &str = "strip";
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Behavior {
|
||||
|
@ -37,6 +39,8 @@ pub struct Behavior {
|
|||
verbose: bool,
|
||||
preserve_timestamps: bool,
|
||||
compare: bool,
|
||||
strip: bool,
|
||||
strip_program: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
|
@ -164,17 +168,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.help("apply access/modification times of SOURCE files to corresponding destination files")
|
||||
)
|
||||
.arg(
|
||||
// TODO implement flag
|
||||
Arg::with_name(OPT_STRIP)
|
||||
.short("s")
|
||||
.long(OPT_STRIP)
|
||||
.help("(unimplemented) strip symbol tables")
|
||||
.help("strip symbol tables (no action Windows)")
|
||||
)
|
||||
.arg(
|
||||
// TODO implement flag
|
||||
Arg::with_name(OPT_STRIP_PROGRAM)
|
||||
.long(OPT_STRIP_PROGRAM)
|
||||
.help("(unimplemented) program used to strip binaries")
|
||||
.help("program used to strip binaries (no action Windows)")
|
||||
.value_name("PROGRAM")
|
||||
)
|
||||
.arg(
|
||||
|
@ -266,10 +268,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
|||
Err("-b")
|
||||
} else if matches.is_present(OPT_CREATED) {
|
||||
Err("-D")
|
||||
} else if matches.is_present(OPT_STRIP) {
|
||||
Err("--strip, -s")
|
||||
} else if matches.is_present(OPT_STRIP_PROGRAM) {
|
||||
Err("--strip-program")
|
||||
} else if matches.is_present(OPT_SUFFIX) {
|
||||
Err("--suffix, -S")
|
||||
} else if matches.is_present(OPT_TARGET_DIRECTORY) {
|
||||
|
@ -304,7 +302,7 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
|||
|
||||
let specified_mode: Option<u32> = if matches.is_present(OPT_MODE) {
|
||||
match matches.value_of(OPT_MODE) {
|
||||
Some(x) => match mode::parse(&x[..], considering_dir) {
|
||||
Some(x) => match mode::parse(x, considering_dir) {
|
||||
Ok(y) => Some(y),
|
||||
Err(err) => {
|
||||
show_error!("Invalid mode string: {}", err);
|
||||
|
@ -339,6 +337,12 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
|||
verbose: matches.is_present(OPT_VERBOSE),
|
||||
preserve_timestamps: matches.is_present(OPT_PRESERVE_TIMESTAMPS),
|
||||
compare: matches.is_present(OPT_COMPARE),
|
||||
strip: matches.is_present(OPT_STRIP),
|
||||
strip_program: String::from(
|
||||
matches
|
||||
.value_of(OPT_STRIP_PROGRAM)
|
||||
.unwrap_or(DEFAULT_STRIP_PROGRAM),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -425,7 +429,7 @@ fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
|||
/// _files_ must all exist as non-directories.
|
||||
/// _target_dir_ must be a directory.
|
||||
///
|
||||
fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behavior) -> i32 {
|
||||
fn copy_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 {
|
||||
if !target_dir.is_dir() {
|
||||
show_error!("target '{}' is not a directory", target_dir.display());
|
||||
return 1;
|
||||
|
@ -449,7 +453,7 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behavior) ->
|
|||
continue;
|
||||
}
|
||||
|
||||
let mut targetpath = target_dir.clone().to_path_buf();
|
||||
let mut targetpath = target_dir.to_path_buf();
|
||||
let filename = sourcepath.components().last().unwrap();
|
||||
targetpath.push(filename);
|
||||
|
||||
|
@ -474,7 +478,7 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behavior) ->
|
|||
/// _file_ must exist as a non-directory.
|
||||
/// _target_ must be a non-directory
|
||||
///
|
||||
fn copy_file_to_file(file: &PathBuf, target: &PathBuf, b: &Behavior) -> i32 {
|
||||
fn copy_file_to_file(file: &Path, target: &Path, b: &Behavior) -> i32 {
|
||||
if copy(file, &target, b).is_err() {
|
||||
1
|
||||
} else {
|
||||
|
@ -493,7 +497,7 @@ fn copy_file_to_file(file: &PathBuf, target: &PathBuf, b: &Behavior) -> i32 {
|
|||
///
|
||||
/// If the copy system call fails, we print a verbose error and return an empty error value.
|
||||
///
|
||||
fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
||||
fn copy(from: &Path, to: &Path, b: &Behavior) -> Result<(), ()> {
|
||||
if b.compare && !need_copy(from, to, b) {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -521,6 +525,21 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
|||
return Err(());
|
||||
}
|
||||
|
||||
if b.strip && cfg!(not(windows)) {
|
||||
match Command::new(&b.strip_program).arg(to).output() {
|
||||
Ok(o) => {
|
||||
if !o.status.success() {
|
||||
crash!(
|
||||
1,
|
||||
"strip program failed: {}",
|
||||
String::from_utf8(o.stderr).unwrap_or_default()
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => crash!(1, "strip program execution failed: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
if mode::chmod(&to, b.mode()).is_err() {
|
||||
return Err(());
|
||||
}
|
||||
|
@ -537,7 +556,7 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
|||
};
|
||||
let gid = meta.gid();
|
||||
match wrap_chown(
|
||||
to.as_path(),
|
||||
to,
|
||||
&meta,
|
||||
Some(owner_id),
|
||||
Some(gid),
|
||||
|
@ -563,7 +582,7 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
|||
Ok(g) => g,
|
||||
_ => crash!(1, "no such group: {}", b.group),
|
||||
};
|
||||
match wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal) {
|
||||
match wrap_chgrp(to, &meta, group_id, false, Verbosity::Normal) {
|
||||
Ok(n) => {
|
||||
if !n.is_empty() {
|
||||
show_info!("{}", n);
|
||||
|
@ -582,7 +601,7 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
|||
let modified_time = FileTime::from_last_modification_time(&meta);
|
||||
let accessed_time = FileTime::from_last_access_time(&meta);
|
||||
|
||||
match set_file_times(to.as_path(), accessed_time, modified_time) {
|
||||
match set_file_times(to, accessed_time, modified_time) {
|
||||
Ok(_) => {}
|
||||
Err(e) => show_info!("{}", e),
|
||||
}
|
||||
|
@ -611,7 +630,7 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
|||
///
|
||||
/// Crashes the program if a nonexistent owner or group is specified in _b_.
|
||||
///
|
||||
fn need_copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> bool {
|
||||
fn need_copy(from: &Path, to: &Path, b: &Behavior) -> bool {
|
||||
let from_meta = match fs::metadata(from) {
|
||||
Ok(meta) => meta,
|
||||
Err(_) => return true,
|
||||
|
|
|
@ -303,7 +303,7 @@ fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
fn link_files_in_dir(files: &[PathBuf], target_dir: &PathBuf, settings: &Settings) -> i32 {
|
||||
fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) -> i32 {
|
||||
if !target_dir.is_dir() {
|
||||
show_error!("target '{}' is not a directory", target_dir.display());
|
||||
return 1;
|
||||
|
@ -329,7 +329,7 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &PathBuf, settings: &Setting
|
|||
};
|
||||
}
|
||||
}
|
||||
target_dir.clone()
|
||||
target_dir.to_path_buf()
|
||||
} else {
|
||||
match srcpath.as_os_str().to_str() {
|
||||
Some(name) => {
|
||||
|
@ -370,7 +370,7 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &PathBuf, settings: &Setting
|
|||
}
|
||||
}
|
||||
|
||||
fn relative_path<'a>(src: &PathBuf, dst: &PathBuf) -> Result<Cow<'a, Path>> {
|
||||
fn relative_path<'a>(src: &Path, dst: &Path) -> Result<Cow<'a, Path>> {
|
||||
let abssrc = canonicalize(src, CanonicalizeMode::Normal)?;
|
||||
let absdst = canonicalize(dst, CanonicalizeMode::Normal)?;
|
||||
let suffix_pos = abssrc
|
||||
|
@ -390,7 +390,7 @@ fn relative_path<'a>(src: &PathBuf, dst: &PathBuf) -> Result<Cow<'a, Path>> {
|
|||
Ok(result.into())
|
||||
}
|
||||
|
||||
fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
|
||||
fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> {
|
||||
let mut backup_path = None;
|
||||
let source: Cow<'_, Path> = if settings.relative {
|
||||
relative_path(&src, dst)?
|
||||
|
@ -453,13 +453,13 @@ fn read_yes() -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn simple_backup_path(path: &PathBuf, suffix: &str) -> PathBuf {
|
||||
fn simple_backup_path(path: &Path, suffix: &str) -> PathBuf {
|
||||
let mut p = path.as_os_str().to_str().unwrap().to_owned();
|
||||
p.push_str(suffix);
|
||||
PathBuf::from(p)
|
||||
}
|
||||
|
||||
fn numbered_backup_path(path: &PathBuf) -> PathBuf {
|
||||
fn numbered_backup_path(path: &Path) -> PathBuf {
|
||||
let mut i: u64 = 1;
|
||||
loop {
|
||||
let new_path = simple_backup_path(path, &format!(".~{}~", i));
|
||||
|
@ -470,7 +470,7 @@ fn numbered_backup_path(path: &PathBuf) -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
fn existing_backup_path(path: &PathBuf, suffix: &str) -> PathBuf {
|
||||
fn existing_backup_path(path: &Path, suffix: &str) -> PathBuf {
|
||||
let test_path = simple_backup_path(path, &".~1~".to_owned());
|
||||
if test_path.exists() {
|
||||
return numbered_backup_path(path);
|
||||
|
|
|
@ -381,6 +381,7 @@ impl Config {
|
|||
})
|
||||
.or_else(|| termsize::get().map(|s| s.cols));
|
||||
|
||||
#[allow(clippy::needless_bool)]
|
||||
let show_control = if options.is_present(options::HIDE_CONTROL_CHARS) {
|
||||
false
|
||||
} else if options.is_present(options::SHOW_CONTROL_CHARS) {
|
||||
|
@ -1108,7 +1109,7 @@ fn sort_entries(entries: &mut Vec<PathBuf>, config: &Config) {
|
|||
}
|
||||
// The default sort in GNU ls is case insensitive
|
||||
Sort::Name => entries.sort_by_key(|k| k.to_string_lossy().to_lowercase()),
|
||||
Sort::Version => entries.sort_by(version_cmp::version_cmp),
|
||||
Sort::Version => entries.sort_by(|a, b| version_cmp::version_cmp(a, b)),
|
||||
Sort::None => {}
|
||||
}
|
||||
|
||||
|
@ -1142,7 +1143,7 @@ fn should_display(entry: &DirEntry, config: &Config) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
fn enter_directory(dir: &PathBuf, config: &Config) {
|
||||
fn enter_directory(dir: &Path, config: &Config) {
|
||||
let mut entries: Vec<_> = safe_unwrap!(fs::read_dir(dir).and_then(Iterator::collect));
|
||||
|
||||
entries.retain(|e| should_display(e, config));
|
||||
|
@ -1167,7 +1168,7 @@ fn enter_directory(dir: &PathBuf, config: &Config) {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_metadata(entry: &PathBuf, dereference: bool) -> std::io::Result<Metadata> {
|
||||
fn get_metadata(entry: &Path, dereference: bool) -> std::io::Result<Metadata> {
|
||||
if dereference {
|
||||
entry.metadata().or_else(|_| entry.symlink_metadata())
|
||||
} else {
|
||||
|
@ -1175,7 +1176,7 @@ fn get_metadata(entry: &PathBuf, dereference: bool) -> std::io::Result<Metadata>
|
|||
}
|
||||
}
|
||||
|
||||
fn display_dir_entry_size(entry: &PathBuf, config: &Config) -> (usize, usize) {
|
||||
fn display_dir_entry_size(entry: &Path, config: &Config) -> (usize, usize) {
|
||||
if let Ok(md) = get_metadata(entry, false) {
|
||||
(
|
||||
display_symlink_count(&md).len(),
|
||||
|
@ -1270,7 +1271,7 @@ fn display_grid(names: impl Iterator<Item = Cell>, width: u16, direction: Direct
|
|||
use uucore::fs::display_permissions;
|
||||
|
||||
fn display_item_long(
|
||||
item: &PathBuf,
|
||||
item: &Path,
|
||||
strip: Option<&Path>,
|
||||
max_links: usize,
|
||||
max_size: usize,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use std::{cmp::Ordering, path::PathBuf};
|
||||
use std::cmp::Ordering;
|
||||
use std::path::Path;
|
||||
|
||||
/// Compare pathbufs in a way that matches the GNU version sort, meaning that
|
||||
/// Compare paths in a way that matches the GNU version sort, meaning that
|
||||
/// numbers get sorted in a natural way.
|
||||
pub(crate) fn version_cmp(a: &PathBuf, b: &PathBuf) -> Ordering {
|
||||
pub(crate) fn version_cmp(a: &Path, b: &Path) -> Ordering {
|
||||
let a_string = a.to_string_lossy();
|
||||
let b_string = b.to_string_lossy();
|
||||
let mut a = a_string.chars().peekable();
|
||||
|
|
|
@ -335,7 +335,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 {
|
|||
0
|
||||
}
|
||||
|
||||
fn move_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behavior) -> i32 {
|
||||
fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 {
|
||||
if !target_dir.is_dir() {
|
||||
show_error!("target ‘{}’ is not a directory", target_dir.display());
|
||||
return 1;
|
||||
|
@ -373,7 +373,7 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behavior) ->
|
|||
}
|
||||
}
|
||||
|
||||
fn rename(from: &PathBuf, to: &PathBuf, b: &Behavior) -> io::Result<()> {
|
||||
fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> {
|
||||
let mut backup_path = None;
|
||||
|
||||
if to.exists() {
|
||||
|
@ -429,7 +429,7 @@ fn rename(from: &PathBuf, to: &PathBuf, b: &Behavior) -> io::Result<()> {
|
|||
|
||||
/// A wrapper around `fs::rename`, so that if it fails, we try falling back on
|
||||
/// copying and removing.
|
||||
fn rename_with_fallback(from: &PathBuf, to: &PathBuf) -> io::Result<()> {
|
||||
fn rename_with_fallback(from: &Path, to: &Path) -> io::Result<()> {
|
||||
if fs::rename(from, to).is_err() {
|
||||
// Get metadata without following symlinks
|
||||
let metadata = from.symlink_metadata()?;
|
||||
|
@ -464,7 +464,7 @@ fn rename_with_fallback(from: &PathBuf, to: &PathBuf) -> io::Result<()> {
|
|||
/// Move the given symlink to the given destination. On Windows, dangling
|
||||
/// symlinks return an error.
|
||||
#[inline]
|
||||
fn rename_symlink_fallback(from: &PathBuf, to: &PathBuf) -> io::Result<()> {
|
||||
fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
|
||||
let path_symlink_points_to = fs::read_link(from)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
|
@ -507,20 +507,20 @@ fn read_yes() -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn simple_backup_path(path: &PathBuf, suffix: &str) -> PathBuf {
|
||||
fn simple_backup_path(path: &Path, suffix: &str) -> PathBuf {
|
||||
let mut p = path.to_string_lossy().into_owned();
|
||||
p.push_str(suffix);
|
||||
PathBuf::from(p)
|
||||
}
|
||||
|
||||
fn numbered_backup_path(path: &PathBuf) -> PathBuf {
|
||||
fn numbered_backup_path(path: &Path) -> PathBuf {
|
||||
(1_u64..)
|
||||
.map(|i| path.with_extension(format!("~{}~", i)))
|
||||
.find(|p| !p.exists())
|
||||
.expect("cannot create backup")
|
||||
}
|
||||
|
||||
fn existing_backup_path(path: &PathBuf, suffix: &str) -> PathBuf {
|
||||
fn existing_backup_path(path: &Path, suffix: &str) -> PathBuf {
|
||||
let test_path = path.with_extension("~1~");
|
||||
if test_path.exists() {
|
||||
numbered_backup_path(path)
|
||||
|
@ -529,7 +529,7 @@ fn existing_backup_path(path: &PathBuf, suffix: &str) -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_empty_dir(path: &PathBuf) -> bool {
|
||||
fn is_empty_dir(path: &Path) -> bool {
|
||||
match fs::read_dir(path) {
|
||||
Ok(contents) => contents.peekable().peek().is_none(),
|
||||
Err(_e) => false,
|
||||
|
|
|
@ -118,7 +118,7 @@ struct OdOptions {
|
|||
}
|
||||
|
||||
impl OdOptions {
|
||||
fn new<'a>(matches: ArgMatches<'a>, args: Vec<String>) -> Result<OdOptions, String> {
|
||||
fn new(matches: ArgMatches, args: Vec<String>) -> Result<OdOptions, String> {
|
||||
let byte_order = match matches.value_of(options::ENDIAN) {
|
||||
None => ByteOrder::Native,
|
||||
Some("little") => ByteOrder::Little,
|
||||
|
|
|
@ -63,7 +63,7 @@ pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs,
|
|||
}
|
||||
if input_strings.len() == 2 {
|
||||
return Ok(CommandLineInputs::FileAndOffset((
|
||||
input_strings[0].clone().to_owned(),
|
||||
input_strings[0].to_string(),
|
||||
n,
|
||||
None,
|
||||
)));
|
||||
|
@ -106,7 +106,7 @@ pub fn parse_inputs_traditional(input_strings: Vec<&str>) -> Result<CommandLineI
|
|||
Some(m),
|
||||
))),
|
||||
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
||||
input_strings[0].clone().to_owned(),
|
||||
input_strings[0].to_string(),
|
||||
m,
|
||||
None,
|
||||
))),
|
||||
|
@ -118,7 +118,7 @@ pub fn parse_inputs_traditional(input_strings: Vec<&str>) -> Result<CommandLineI
|
|||
let label = parse_offset_operand(&input_strings[2]);
|
||||
match (offset, label) {
|
||||
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
|
||||
input_strings[0].clone().to_owned(),
|
||||
input_strings[0].to_string(),
|
||||
n,
|
||||
Some(m),
|
||||
))),
|
||||
|
|
|
@ -15,7 +15,6 @@ use uucore::utmpx::{self, time, Utmpx};
|
|||
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::io::Result as IOResult;
|
||||
|
||||
use std::fs::File;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
@ -136,12 +135,8 @@ The utmp file will be {}",
|
|||
};
|
||||
|
||||
if do_short_format {
|
||||
if let Err(e) = pk.short_pinky() {
|
||||
show_usage_error!("{}", e);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
pk.short_pinky();
|
||||
0
|
||||
} else {
|
||||
pk.long_pinky()
|
||||
}
|
||||
|
@ -282,7 +277,7 @@ impl Pinky {
|
|||
println!();
|
||||
}
|
||||
|
||||
fn short_pinky(&self) -> IOResult<()> {
|
||||
fn short_pinky(&self) {
|
||||
if self.include_heading {
|
||||
self.print_heading();
|
||||
}
|
||||
|
@ -295,7 +290,6 @@ impl Pinky {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn long_pinky(&self) -> i32 {
|
||||
|
|
|
@ -199,8 +199,7 @@ pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<
|
|||
}
|
||||
|
||||
pub fn base_conv_vec(src: &[u8], radix_src: u8, radix_dest: u8) -> Vec<u8> {
|
||||
let mut result: Vec<u8> = Vec::new();
|
||||
result.push(0);
|
||||
let mut result = vec![0];
|
||||
for i in src {
|
||||
result = arrnum_int_mult(&result, radix_dest, radix_src);
|
||||
result = arrnum_int_add(&result, radix_dest, *i);
|
||||
|
@ -226,8 +225,7 @@ pub fn base_conv_float(src: &[u8], radix_src: u8, radix_dest: u8) -> f64 {
|
|||
// to implement this for arbitrary string input.
|
||||
// until then, the below operates as an outline
|
||||
// of how it would work.
|
||||
let mut result: Vec<u8> = Vec::new();
|
||||
result.push(0);
|
||||
let result: Vec<u8> = vec![0];
|
||||
let mut factor: f64 = 1_f64;
|
||||
let radix_src_float: f64 = f64::from(radix_src);
|
||||
let mut r: f64 = 0_f64;
|
||||
|
|
|
@ -263,9 +263,5 @@ pub fn num_format(field: &FormatField, in_str_opt: Option<&String>) -> Option<St
|
|||
};
|
||||
// if we have a formatPrimitive, print its results
|
||||
// according to the field-char appropriate Formatter
|
||||
if let Some(prim) = prim_opt {
|
||||
Some(fmtr.primitive_to_str(&prim, field.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
prim_opt.map(|prim| fmtr.primitive_to_str(&prim, field.clone()))
|
||||
}
|
||||
|
|
|
@ -177,14 +177,14 @@ fn get_config(matches: &clap::ArgMatches) -> Config {
|
|||
}
|
||||
if matches.is_present(options::WIDTH) {
|
||||
let width_str = matches.value_of(options::WIDTH).expect(err_msg).to_string();
|
||||
config.line_width = crash_if_err!(1, usize::from_str_radix(&width_str, 10));
|
||||
config.line_width = crash_if_err!(1, (&width_str).parse::<usize>());
|
||||
}
|
||||
if matches.is_present(options::GAP_SIZE) {
|
||||
let gap_str = matches
|
||||
.value_of(options::GAP_SIZE)
|
||||
.expect(err_msg)
|
||||
.to_string();
|
||||
config.gap_size = crash_if_err!(1, usize::from_str_radix(&gap_str, 10));
|
||||
config.gap_size = crash_if_err!(1, (&gap_str).parse::<usize>());
|
||||
}
|
||||
if matches.is_present(options::FORMAT_ROFF) {
|
||||
config.format = OutFormat::Roff;
|
||||
|
|
|
@ -13,7 +13,7 @@ extern crate uucore;
|
|||
use clap::{App, Arg};
|
||||
use std::fs;
|
||||
use std::io::{stdout, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::fs::{canonicalize, CanonicalizeMode};
|
||||
|
||||
const NAME: &str = "readlink";
|
||||
|
@ -160,8 +160,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
0
|
||||
}
|
||||
|
||||
fn show(path: &PathBuf, no_newline: bool, use_zero: bool) {
|
||||
let path = path.as_path().to_str().unwrap();
|
||||
fn show(path: &Path, no_newline: bool, use_zero: bool) {
|
||||
let path = path.to_str().unwrap();
|
||||
if use_zero {
|
||||
print!("{}\0", path);
|
||||
} else if no_newline {
|
||||
|
|
|
@ -12,7 +12,7 @@ extern crate uucore;
|
|||
|
||||
use clap::{App, Arg};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::fs::{canonicalize, CanonicalizeMode};
|
||||
|
||||
static ABOUT: &str = "print the resolved path";
|
||||
|
@ -82,7 +82,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
retcode
|
||||
}
|
||||
|
||||
fn resolve_path(p: &PathBuf, strip: bool, zero: bool, quiet: bool) -> bool {
|
||||
fn resolve_path(p: &Path, strip: bool, zero: bool, quiet: bool) -> bool {
|
||||
let abs = canonicalize(p, CanonicalizeMode::Normal).unwrap();
|
||||
|
||||
if strip {
|
||||
|
|
|
@ -176,7 +176,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
} else if matches.is_present(OPT_PROMPT_MORE) {
|
||||
InteractiveMode::Once
|
||||
} else if matches.is_present(OPT_INTERACTIVE) {
|
||||
match &matches.value_of(OPT_INTERACTIVE).unwrap()[..] {
|
||||
match matches.value_of(OPT_INTERACTIVE).unwrap() {
|
||||
"none" => InteractiveMode::None,
|
||||
"once" => InteractiveMode::Once,
|
||||
"always" => InteractiveMode::Always,
|
||||
|
|
|
@ -102,7 +102,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let mut largest_dec = 0;
|
||||
let mut padding = 0;
|
||||
let first = if numbers.len() > 1 {
|
||||
let slice = &numbers[0][..];
|
||||
let slice = numbers[0];
|
||||
let len = slice.len();
|
||||
let dec = slice.find('.').unwrap_or(len);
|
||||
largest_dec = len - dec;
|
||||
|
@ -118,7 +118,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
1.0
|
||||
};
|
||||
let increment = if numbers.len() > 2 {
|
||||
let slice = &numbers[1][..];
|
||||
let slice = numbers[1];
|
||||
let len = slice.len();
|
||||
let dec = slice.find('.').unwrap_or(len);
|
||||
largest_dec = cmp::max(largest_dec, len - dec);
|
||||
|
@ -134,11 +134,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
1.0
|
||||
};
|
||||
if increment == 0.0 {
|
||||
show_error!("increment value: '{}'", &numbers[1][..]);
|
||||
show_error!("increment value: '{}'", numbers[1]);
|
||||
return 1;
|
||||
}
|
||||
let last = {
|
||||
let slice = &numbers[numbers.len() - 1][..];
|
||||
let slice = numbers[numbers.len() - 1];
|
||||
padding = cmp::max(padding, slice.find('.').unwrap_or_else(|| slice.len()));
|
||||
match parse_float(slice) {
|
||||
Ok(n) => n,
|
||||
|
|
|
@ -259,6 +259,7 @@ static AFTER_HELP: &str =
|
|||
";
|
||||
|
||||
pub mod options {
|
||||
pub const FORCE: &str = "force";
|
||||
pub const FILE: &str = "file";
|
||||
pub const ITERATIONS: &str = "iterations";
|
||||
pub const SIZE: &str = "size";
|
||||
|
@ -278,6 +279,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.about(ABOUT)
|
||||
.after_help(AFTER_HELP)
|
||||
.usage(&usage[..])
|
||||
.arg(
|
||||
Arg::with_name(options::FORCE)
|
||||
.long(options::FORCE)
|
||||
.short("f")
|
||||
.help("change permissions to allow writing if necessary"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::ITERATIONS)
|
||||
.long(options::ITERATIONS)
|
||||
|
@ -354,13 +361,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
// TODO: implement --random-source
|
||||
|
||||
// TODO: implement --force
|
||||
|
||||
let force = matches.is_present(options::FORCE);
|
||||
let remove = matches.is_present(options::REMOVE);
|
||||
let size_arg = match matches.value_of(options::SIZE) {
|
||||
Some(s) => Some(s.to_string()),
|
||||
None => None,
|
||||
};
|
||||
let size_arg = matches.value_of(options::SIZE).map(|s| s.to_string());
|
||||
let size = get_size(size_arg);
|
||||
let exact = matches.is_present(options::EXACT) && size.is_none(); // if -s is given, ignore -x
|
||||
let zero = matches.is_present(options::ZERO);
|
||||
|
@ -375,7 +378,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
for path_str in matches.values_of(options::FILE).unwrap() {
|
||||
wipe_file(&path_str, iterations, remove, size, exact, zero, verbose);
|
||||
wipe_file(
|
||||
&path_str, iterations, remove, size, exact, zero, verbose, force,
|
||||
);
|
||||
}
|
||||
|
||||
0
|
||||
|
@ -431,6 +436,7 @@ fn pass_name(pass_type: PassType) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn wipe_file(
|
||||
path_str: &str,
|
||||
n_passes: usize,
|
||||
|
@ -439,18 +445,37 @@ fn wipe_file(
|
|||
exact: bool,
|
||||
zero: bool,
|
||||
verbose: bool,
|
||||
force: bool,
|
||||
) {
|
||||
// Get these potential errors out of the way first
|
||||
let path: &Path = Path::new(path_str);
|
||||
if !path.exists() {
|
||||
println!("{}: {}: No such file or directory", NAME, path.display());
|
||||
show_error!("{}: No such file or directory", path.display());
|
||||
return;
|
||||
}
|
||||
if !path.is_file() {
|
||||
println!("{}: {}: Not a file", NAME, path.display());
|
||||
show_error!("{}: Not a file", path.display());
|
||||
return;
|
||||
}
|
||||
|
||||
// If force is true, set file permissions to not-readonly.
|
||||
if force {
|
||||
let metadata = match fs::metadata(path) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
show_error!("{}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut perms = metadata.permissions();
|
||||
perms.set_readonly(false);
|
||||
if let Err(e) = fs::set_permissions(path, perms) {
|
||||
show_error!("{}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill up our pass sequence
|
||||
let mut pass_sequence: Vec<PassType> = Vec::new();
|
||||
|
||||
|
@ -489,11 +514,13 @@ fn wipe_file(
|
|||
|
||||
{
|
||||
let total_passes: usize = pass_sequence.len();
|
||||
let mut file: File = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(false)
|
||||
.open(path)
|
||||
.expect("Failed to open file for writing");
|
||||
let mut file: File = match OpenOptions::new().write(true).truncate(false).open(path) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
show_error!("{}: failed to open for writing: {}", path.display(), e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: it does not really matter what we set for total_bytes and gen_type here, so just
|
||||
// use bogus values
|
||||
|
@ -523,14 +550,23 @@ fn wipe_file(
|
|||
}
|
||||
}
|
||||
// size is an optional argument for exactly how many bytes we want to shred
|
||||
do_pass(&mut file, path, &mut generator, *pass_type, size)
|
||||
.expect("File write pass failed");
|
||||
match do_pass(&mut file, path, &mut generator, *pass_type, size) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
show_error!("{}: File write pass failed: {}", path.display(), e);
|
||||
}
|
||||
}
|
||||
// Ignore failed writes; just keep trying
|
||||
}
|
||||
}
|
||||
|
||||
if remove {
|
||||
do_remove(path, path_str, verbose).expect("Failed to remove file");
|
||||
match do_remove(path, path_str, verbose) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
show_error!("{}: failed to remove file: {}", path.display(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
33
src/uu/sort/BENCHMARKING.md
Normal file
33
src/uu/sort/BENCHMARKING.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Benchmarking sort
|
||||
|
||||
Most of the time when sorting is spent comparing lines. The comparison functions however differ based
|
||||
on which arguments are passed to `sort`, therefore it is important to always benchmark multiple scenarios.
|
||||
This is an overwiew over what was benchmarked, and if you make changes to `sort`, you are encouraged to check
|
||||
how performance was affected for the workloads listed below. Feel free to add other workloads to the
|
||||
list that we should improve / make sure not to regress.
|
||||
|
||||
Run `cargo build --release` before benchmarking after you make a change!
|
||||
|
||||
## Sorting a wordlist
|
||||
- Get a wordlist, for example with [words](https://en.wikipedia.org/wiki/Words_(Unix)) on Linux. The exact wordlist
|
||||
doesn't matter for performance comparisons. In this example I'm using `/usr/share/dict/american-english` as the wordlist.
|
||||
- Shuffle the wordlist by running `sort -R /usr/share/dict/american-english > shuffled_wordlist.txt`.
|
||||
- Benchmark sorting the wordlist with hyperfine: `hyperfine "target/release/coreutils sort shuffled_wordlist.txt -o output.txt"`.
|
||||
|
||||
## Sorting a wordlist with ignore_case
|
||||
- Same wordlist as above
|
||||
- Benchmark sorting the wordlist ignoring the case with hyperfine: `hyperfine "target/release/coreutils sort shuffled_wordlist.txt -f -o output.txt"`.
|
||||
|
||||
## Sorting numbers
|
||||
- Generate a list of numbers: `seq 0 100000 | sort -R > shuffled_numbers.txt`.
|
||||
- Benchmark numeric sorting with hyperfine: `hyperfine "target/release/coreutils sort shuffled_numbers.txt -n -o output.txt"`.
|
||||
|
||||
## Stdout and stdin performance
|
||||
Try to run the above benchmarks by piping the input through stdin (standard input) and redirect the
|
||||
output through stdout (standard output):
|
||||
- Remove the input file from the arguments and add `cat [inputfile] | ` at the beginning.
|
||||
- Remove `-o output.txt` and add `> output.txt` at the end.
|
||||
|
||||
Example: `hyperfine "target/release/coreutils sort shuffled_numbers.txt -n -o output.txt"` becomes
|
||||
`hyperfine "cat shuffled_numbers.txt | target/release/coreutils sort -n > output.txt`
|
||||
- Check that performance is similar to the original benchmark.
|
|
@ -19,8 +19,9 @@ rayon = "1.5"
|
|||
rand = "0.7"
|
||||
clap = "2.33"
|
||||
fnv = "1.0.7"
|
||||
itertools = "0.8.0"
|
||||
itertools = "0.10.0"
|
||||
semver = "0.9.0"
|
||||
smallvec = "1.6.1"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/stdbuf.rs"
|
||||
|
||||
[dependencies]
|
||||
getopts = "0.2.18"
|
||||
clap = "2.33"
|
||||
tempfile = "3.1"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
|
|
@ -35,8 +35,8 @@ extern "C" {
|
|||
|
||||
fn set_buffer(stream: *mut FILE, value: &str) {
|
||||
let (mode, size): (c_int, size_t) = match value {
|
||||
"0" => (_IONBF, 0 as size_t),
|
||||
"L" => (_IOLBF, 0 as size_t),
|
||||
"0" => (_IONBF, 0_usize),
|
||||
"L" => (_IOLBF, 0_usize),
|
||||
input => {
|
||||
let buff_size: usize = match input.parse() {
|
||||
Ok(num) => num,
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use getopts::{Matches, Options};
|
||||
use clap::{App, AppSettings, Arg, ArgMatches};
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Write};
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
|
@ -19,8 +20,35 @@ use std::process::Command;
|
|||
use tempfile::tempdir;
|
||||
use tempfile::TempDir;
|
||||
|
||||
static NAME: &str = "stdbuf";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static ABOUT: &str =
|
||||
"Run COMMAND, with modified buffering operations for its standard streams.\n\n\
|
||||
Mandatory arguments to long options are mandatory for short options too.";
|
||||
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
|
||||
This option is invalid with standard input.\n\n\
|
||||
If MODE is '0' the corresponding stream will be unbuffered.\n\n\
|
||||
Otherwise MODE is a number which may be followed by one of the following:\n\n\
|
||||
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
|
||||
In this case the corresponding stream will be fully buffered with the buffer size set to \
|
||||
MODE bytes.\n\n\
|
||||
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
|
||||
that will override corresponding settings changed by 'stdbuf'.\n\
|
||||
Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
|
||||
and are thus unaffected by 'stdbuf' settings.\n";
|
||||
|
||||
mod options {
|
||||
pub const INPUT: &str = "input";
|
||||
pub const INPUT_SHORT: &str = "i";
|
||||
pub const OUTPUT: &str = "output";
|
||||
pub const OUTPUT_SHORT: &str = "o";
|
||||
pub const ERROR: &str = "error";
|
||||
pub const ERROR_SHORT: &str = "e";
|
||||
pub const COMMAND: &str = "command";
|
||||
}
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{0} OPTION... COMMAND", executable!())
|
||||
}
|
||||
|
||||
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));
|
||||
|
||||
|
@ -36,16 +64,19 @@ struct ProgramOptions {
|
|||
stderr: BufferType,
|
||||
}
|
||||
|
||||
enum ErrMsg {
|
||||
Retry,
|
||||
Fatal,
|
||||
impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
|
||||
type Error = ProgramOptionsError;
|
||||
|
||||
fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
|
||||
Ok(ProgramOptions {
|
||||
stdin: check_option(&matches, options::INPUT)?,
|
||||
stdout: check_option(&matches, options::OUTPUT)?,
|
||||
stderr: check_option(&matches, options::ERROR)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enum OkMsg {
|
||||
Buffering,
|
||||
Help,
|
||||
Version,
|
||||
}
|
||||
struct ProgramOptionsError(String);
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
|
@ -73,31 +104,6 @@ fn preload_strings() -> (&'static str, &'static str) {
|
|||
crash!(1, "Command not supported for this operating system!")
|
||||
}
|
||||
|
||||
fn print_version() {
|
||||
println!("{} {}", NAME, VERSION);
|
||||
}
|
||||
|
||||
fn print_usage(opts: &Options) {
|
||||
let brief = "Run COMMAND, with modified buffering operations for its standard streams\n \
|
||||
Mandatory arguments to long options are mandatory for short options too.";
|
||||
let explanation = "If MODE is 'L' the corresponding stream will be line buffered.\n \
|
||||
This option is invalid with standard input.\n\n \
|
||||
If MODE is '0' the corresponding stream will be unbuffered.\n\n \
|
||||
Otherwise MODE is a number which may be followed by one of the following:\n\n \
|
||||
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n \
|
||||
In this case the corresponding stream will be fully buffered with the buffer size set to \
|
||||
MODE bytes.\n\n \
|
||||
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
|
||||
that will override corresponding settings changed by 'stdbuf'.\n \
|
||||
Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
|
||||
and are thus unaffected by 'stdbuf' settings.\n";
|
||||
println!("{} {}", NAME, VERSION);
|
||||
println!();
|
||||
println!("Usage: stdbuf OPTION... COMMAND");
|
||||
println!();
|
||||
println!("{}\n{}", opts.usage(brief), explanation);
|
||||
}
|
||||
|
||||
fn parse_size(size: &str) -> Option<u64> {
|
||||
let ext = size.trim_start_matches(|c: char| c.is_digit(10));
|
||||
let num = size.trim_end_matches(char::is_alphabetic);
|
||||
|
@ -133,65 +139,28 @@ fn parse_size(size: &str) -> Option<u64> {
|
|||
Some(buf_size * base.pow(power))
|
||||
}
|
||||
|
||||
fn check_option(matches: &Matches, name: &str, modified: &mut bool) -> Option<BufferType> {
|
||||
match matches.opt_str(name) {
|
||||
Some(value) => {
|
||||
*modified = true;
|
||||
match &value[..] {
|
||||
"L" => {
|
||||
if name == "input" {
|
||||
show_info!("line buffering stdin is meaningless");
|
||||
None
|
||||
} else {
|
||||
Some(BufferType::Line)
|
||||
}
|
||||
}
|
||||
x => {
|
||||
let size = match parse_size(x) {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
show_error!("Invalid mode {}", x);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(BufferType::Size(size))
|
||||
fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
|
||||
match matches.value_of(name) {
|
||||
Some(value) => match value {
|
||||
"L" => {
|
||||
if name == options::INPUT {
|
||||
Err(ProgramOptionsError("line buffering stdin is meaningless".to_string()))
|
||||
} else {
|
||||
Ok(BufferType::Line)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Some(BufferType::Default),
|
||||
x => {
|
||||
let size = match parse_size(x) {
|
||||
Some(m) => m,
|
||||
None => return Err(ProgramOptionsError(format!("invalid mode {}", x))),
|
||||
};
|
||||
Ok(BufferType::Size(size))
|
||||
}
|
||||
},
|
||||
None => Ok(BufferType::Default),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_options(
|
||||
args: &[String],
|
||||
options: &mut ProgramOptions,
|
||||
optgrps: &Options,
|
||||
) -> Result<OkMsg, ErrMsg> {
|
||||
let matches = match optgrps.parse(args) {
|
||||
Ok(m) => m,
|
||||
Err(_) => return Err(ErrMsg::Retry),
|
||||
};
|
||||
if matches.opt_present("help") {
|
||||
return Ok(OkMsg::Help);
|
||||
}
|
||||
if matches.opt_present("version") {
|
||||
return Ok(OkMsg::Version);
|
||||
}
|
||||
let mut modified = false;
|
||||
options.stdin = check_option(&matches, "input", &mut modified).ok_or(ErrMsg::Fatal)?;
|
||||
options.stdout = check_option(&matches, "output", &mut modified).ok_or(ErrMsg::Fatal)?;
|
||||
options.stderr = check_option(&matches, "error", &mut modified).ok_or(ErrMsg::Fatal)?;
|
||||
|
||||
if matches.free.len() != 1 {
|
||||
return Err(ErrMsg::Retry);
|
||||
}
|
||||
if !modified {
|
||||
show_error!("you must specify a buffering mode option");
|
||||
return Err(ErrMsg::Fatal);
|
||||
}
|
||||
Ok(OkMsg::Buffering)
|
||||
}
|
||||
|
||||
fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
|
||||
match buffer_type {
|
||||
BufferType::Size(m) => {
|
||||
|
@ -215,72 +184,62 @@ fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
|
|||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let args = args.collect_str();
|
||||
let usage = get_usage();
|
||||
|
||||
let mut opts = Options::new();
|
||||
let matches = App::new(executable!())
|
||||
.version(VERSION)
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.after_help(LONG_HELP)
|
||||
.setting(AppSettings::TrailingVarArg)
|
||||
.arg(
|
||||
Arg::with_name(options::INPUT)
|
||||
.long(options::INPUT)
|
||||
.short(options::INPUT_SHORT)
|
||||
.help("adjust standard input stream buffering")
|
||||
.value_name("MODE")
|
||||
.required_unless_one(&[options::OUTPUT, options::ERROR]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::OUTPUT)
|
||||
.long(options::OUTPUT)
|
||||
.short(options::OUTPUT_SHORT)
|
||||
.help("adjust standard output stream buffering")
|
||||
.value_name("MODE")
|
||||
.required_unless_one(&[options::INPUT, options::ERROR]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::ERROR)
|
||||
.long(options::ERROR)
|
||||
.short(options::ERROR_SHORT)
|
||||
.help("adjust standard error stream buffering")
|
||||
.value_name("MODE")
|
||||
.required_unless_one(&[options::INPUT, options::OUTPUT]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::COMMAND)
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.hidden(true)
|
||||
.required(true),
|
||||
)
|
||||
.get_matches_from(args);
|
||||
|
||||
opts.optopt(
|
||||
"i",
|
||||
"input",
|
||||
"adjust standard input stream buffering",
|
||||
"MODE",
|
||||
);
|
||||
opts.optopt(
|
||||
"o",
|
||||
"output",
|
||||
"adjust standard output stream buffering",
|
||||
"MODE",
|
||||
);
|
||||
opts.optopt(
|
||||
"e",
|
||||
"error",
|
||||
"adjust standard error stream buffering",
|
||||
"MODE",
|
||||
);
|
||||
opts.optflag("", "help", "display this help and exit");
|
||||
opts.optflag("", "version", "output version information and exit");
|
||||
let options = ProgramOptions::try_from(&matches)
|
||||
.unwrap_or_else(|e| crash!(125, "{}\nTry 'stdbuf --help' for more information.", e.0));
|
||||
|
||||
let mut options = ProgramOptions {
|
||||
stdin: BufferType::Default,
|
||||
stdout: BufferType::Default,
|
||||
stderr: BufferType::Default,
|
||||
};
|
||||
let mut command_idx: i32 = -1;
|
||||
for i in 1..=args.len() {
|
||||
match parse_options(&args[1..i], &mut options, &opts) {
|
||||
Ok(OkMsg::Buffering) => {
|
||||
command_idx = (i as i32) - 1;
|
||||
break;
|
||||
}
|
||||
Ok(OkMsg::Help) => {
|
||||
print_usage(&opts);
|
||||
return 0;
|
||||
}
|
||||
Ok(OkMsg::Version) => {
|
||||
print_version();
|
||||
return 0;
|
||||
}
|
||||
Err(ErrMsg::Fatal) => break,
|
||||
Err(ErrMsg::Retry) => continue,
|
||||
}
|
||||
}
|
||||
if command_idx == -1 {
|
||||
crash!(
|
||||
125,
|
||||
"Invalid options\nTry 'stdbuf --help' for more information."
|
||||
);
|
||||
}
|
||||
let command_name = &args[command_idx as usize];
|
||||
let mut command = Command::new(command_name);
|
||||
let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap();
|
||||
let mut command = Command::new(command_values.next().unwrap());
|
||||
let command_params: Vec<&str> = command_values.collect();
|
||||
|
||||
let mut tmp_dir = tempdir().unwrap();
|
||||
let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir));
|
||||
command
|
||||
.args(&args[(command_idx as usize) + 1..])
|
||||
.env(preload_env, libstdbuf);
|
||||
command.env(preload_env, libstdbuf);
|
||||
set_command_env(&mut command, "_STDBUF_I", options.stdin);
|
||||
set_command_env(&mut command, "_STDBUF_O", options.stdout);
|
||||
set_command_env(&mut command, "_STDBUF_E", options.stderr);
|
||||
command.args(command_params);
|
||||
|
||||
let mut process = match command.spawn() {
|
||||
Ok(p) => p,
|
||||
Err(e) => crash!(1, "failed to execute process: {}", e),
|
||||
|
|
|
@ -75,7 +75,7 @@ fn open(name: &str) -> Result<Box<dyn Read>> {
|
|||
"Is a directory",
|
||||
));
|
||||
};
|
||||
if !path.metadata().is_ok() {
|
||||
if path.metadata().is_err() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"No such file or directory",
|
||||
|
|
|
@ -90,7 +90,7 @@ fn tac(filenames: Vec<String>, before: bool, _: bool, separator: &str) -> i32 {
|
|||
Box::new(stdin()) as Box<dyn Read>
|
||||
} else {
|
||||
let path = Path::new(filename);
|
||||
if path.is_dir() || !path.metadata().is_ok() {
|
||||
if path.is_dir() || path.metadata().is_err() {
|
||||
show_error!(
|
||||
"failed to open '{}' for reading: No such file or directory",
|
||||
filename
|
||||
|
|
|
@ -55,16 +55,16 @@ fn two(args: &[&[u8]], error: &mut bool) -> bool {
|
|||
b"-d" => path(args[1], PathCondition::Directory),
|
||||
b"-e" => path(args[1], PathCondition::Exists),
|
||||
b"-f" => path(args[1], PathCondition::Regular),
|
||||
b"-g" => path(args[1], PathCondition::GroupIDFlag),
|
||||
b"-g" => path(args[1], PathCondition::GroupIdFlag),
|
||||
b"-h" => path(args[1], PathCondition::SymLink),
|
||||
b"-L" => path(args[1], PathCondition::SymLink),
|
||||
b"-n" => one(&args[1..]),
|
||||
b"-p" => path(args[1], PathCondition::FIFO),
|
||||
b"-p" => path(args[1], PathCondition::Fifo),
|
||||
b"-r" => path(args[1], PathCondition::Readable),
|
||||
b"-S" => path(args[1], PathCondition::Socket),
|
||||
b"-s" => path(args[1], PathCondition::NonEmpty),
|
||||
b"-t" => isatty(args[1]),
|
||||
b"-u" => path(args[1], PathCondition::UserIDFlag),
|
||||
b"-u" => path(args[1], PathCondition::UserIdFlag),
|
||||
b"-w" => path(args[1], PathCondition::Writable),
|
||||
b"-x" => path(args[1], PathCondition::Executable),
|
||||
b"-z" => !one(&args[1..]),
|
||||
|
@ -322,13 +322,13 @@ enum PathCondition {
|
|||
Directory,
|
||||
Exists,
|
||||
Regular,
|
||||
GroupIDFlag,
|
||||
GroupIdFlag,
|
||||
SymLink,
|
||||
FIFO,
|
||||
Fifo,
|
||||
Readable,
|
||||
Socket,
|
||||
NonEmpty,
|
||||
UserIDFlag,
|
||||
UserIdFlag,
|
||||
Writable,
|
||||
Executable,
|
||||
}
|
||||
|
@ -390,13 +390,13 @@ fn path(path: &[u8], cond: PathCondition) -> bool {
|
|||
PathCondition::Directory => file_type.is_dir(),
|
||||
PathCondition::Exists => true,
|
||||
PathCondition::Regular => file_type.is_file(),
|
||||
PathCondition::GroupIDFlag => metadata.mode() & S_ISGID != 0,
|
||||
PathCondition::GroupIdFlag => metadata.mode() & S_ISGID != 0,
|
||||
PathCondition::SymLink => metadata.file_type().is_symlink(),
|
||||
PathCondition::FIFO => file_type.is_fifo(),
|
||||
PathCondition::Fifo => file_type.is_fifo(),
|
||||
PathCondition::Readable => perm(metadata, Permission::Read),
|
||||
PathCondition::Socket => file_type.is_socket(),
|
||||
PathCondition::NonEmpty => metadata.size() > 0,
|
||||
PathCondition::UserIDFlag => metadata.mode() & S_ISUID != 0,
|
||||
PathCondition::UserIdFlag => metadata.mode() & S_ISUID != 0,
|
||||
PathCondition::Writable => perm(metadata, Permission::Write),
|
||||
PathCondition::Executable => perm(metadata, Permission::Execute),
|
||||
}
|
||||
|
@ -416,13 +416,13 @@ fn path(path: &[u8], cond: PathCondition) -> bool {
|
|||
PathCondition::Directory => stat.is_dir(),
|
||||
PathCondition::Exists => true,
|
||||
PathCondition::Regular => stat.is_file(),
|
||||
PathCondition::GroupIDFlag => false,
|
||||
PathCondition::GroupIdFlag => false,
|
||||
PathCondition::SymLink => false,
|
||||
PathCondition::FIFO => false,
|
||||
PathCondition::Fifo => false,
|
||||
PathCondition::Readable => false, // TODO
|
||||
PathCondition::Socket => false,
|
||||
PathCondition::NonEmpty => stat.len() > 0,
|
||||
PathCondition::UserIDFlag => false,
|
||||
PathCondition::UserIdFlag => false,
|
||||
PathCondition::Writable => false, // TODO
|
||||
PathCondition::Executable => false, // TODO
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ use filetime::*;
|
|||
use std::fs::{self, File};
|
||||
use std::io::Error;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static ABOUT: &str = "Update the access and modification times of each FILE to the current time.";
|
||||
|
@ -137,7 +138,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
let (mut atime, mut mtime) = if matches.is_present(options::sources::REFERENCE) {
|
||||
stat(
|
||||
&matches.value_of(options::sources::REFERENCE).unwrap()[..],
|
||||
matches.value_of(options::sources::REFERENCE).unwrap(),
|
||||
!matches.is_present(options::NO_DEREF),
|
||||
)
|
||||
} else if matches.is_present(options::sources::DATE)
|
||||
|
@ -261,7 +262,27 @@ fn parse_timestamp(s: &str) -> FileTime {
|
|||
};
|
||||
|
||||
match time::strptime(&ts, format) {
|
||||
Ok(tm) => local_tm_to_filetime(to_local(tm)),
|
||||
Ok(tm) => {
|
||||
let mut local = to_local(tm);
|
||||
local.tm_isdst = -1;
|
||||
let ft = local_tm_to_filetime(local);
|
||||
|
||||
// We have to check that ft is valid time. Due to daylight saving
|
||||
// time switch, local time can jump from 1:59 AM to 3:00 AM,
|
||||
// in which case any time between 2:00 AM and 2:59 AM is not valid.
|
||||
// Convert back to local time and see if we got the same value back.
|
||||
let ts = time::Timespec {
|
||||
sec: ft.unix_seconds(),
|
||||
nsec: 0,
|
||||
};
|
||||
let tm2 = time::at(ts);
|
||||
if tm.tm_hour != tm2.tm_hour {
|
||||
show_error!("invalid date format {}", s);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
ft
|
||||
}
|
||||
Err(e) => panic!("Unable to parse timestamp\n{}", e),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ use std::ops::RangeInclusive;
|
|||
fn parse_sequence(s: &str) -> (char, usize) {
|
||||
let c = s.chars().next().expect("invalid escape: empty string");
|
||||
|
||||
if '0' <= c && c <= '7' {
|
||||
if ('0'..='7').contains(&c) {
|
||||
let mut v = c.to_digit(8).unwrap();
|
||||
let mut consumed = 1;
|
||||
let bits_per_digit = 3;
|
||||
|
|
|
@ -16,8 +16,8 @@ use std::io::{stdin, BufRead, BufReader, Read};
|
|||
use std::path::Path;
|
||||
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static SUMMARY: &str = "Topological sort the strings in FILE.
|
||||
Strings are defined as any sequence of tokens separated by whitespace (tab, space, or newline).
|
||||
static SUMMARY: &str = "Topological sort the strings in FILE.
|
||||
Strings are defined as any sequence of tokens separated by whitespace (tab, space, or newline).
|
||||
If FILE is not passed in, stdin is used instead.";
|
||||
static USAGE: &str = "tsort [OPTIONS] FILE";
|
||||
|
||||
|
@ -32,13 +32,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
.version(VERSION)
|
||||
.usage(USAGE)
|
||||
.about(SUMMARY)
|
||||
.arg(Arg::with_name(options::FILE).hidden(true))
|
||||
.arg(
|
||||
Arg::with_name(options::FILE)
|
||||
.default_value("-")
|
||||
.hidden(true),
|
||||
)
|
||||
.get_matches_from(args);
|
||||
|
||||
let input = match matches.value_of(options::FILE) {
|
||||
Some(v) => v,
|
||||
None => "-",
|
||||
};
|
||||
let input = matches
|
||||
.value_of(options::FILE)
|
||||
.expect("Value is required by clap");
|
||||
|
||||
let mut stdin_buf;
|
||||
let mut file_buf;
|
||||
|
|
|
@ -65,9 +65,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
return if is_stdin_interactive() {
|
||||
if is_stdin_interactive() {
|
||||
libc::EXIT_SUCCESS
|
||||
} else {
|
||||
libc::EXIT_FAILURE
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,10 +149,8 @@ fn next_tabstop(tabstops: &[usize], col: usize) -> Option<usize> {
|
|||
Some(tabstops[0] - col % tabstops[0])
|
||||
} else {
|
||||
// find next larger tab
|
||||
match tabstops.iter().find(|&&t| t > col) {
|
||||
Some(t) => Some(t - col),
|
||||
None => None, // if there isn't one in the list, tab becomes a single space
|
||||
}
|
||||
// if there isn't one in the list, tab becomes a single space
|
||||
tabstops.iter().find(|&&t| t > col).map(|t| t-col)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ edition = "2018"
|
|||
path = "src/unlink.rs"
|
||||
|
||||
[dependencies]
|
||||
getopts = "0.2.18"
|
||||
clap = "2.33"
|
||||
libc = "0.2.42"
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
|
|
@ -12,59 +12,53 @@
|
|||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use getopts::Options;
|
||||
use clap::{App, Arg};
|
||||
use libc::{lstat, stat, unlink};
|
||||
use libc::{S_IFLNK, S_IFMT, S_IFREG};
|
||||
use std::ffi::CString;
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
static NAME: &str = "unlink";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static ABOUT: &str = "Unlink the file at [FILE].";
|
||||
static OPT_PATH: &str = "FILE";
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!("{} [OPTION]... FILE", executable!())
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let args = args.collect_str();
|
||||
|
||||
let mut opts = Options::new();
|
||||
let usage = get_usage();
|
||||
|
||||
opts.optflag("h", "help", "display this help and exit");
|
||||
opts.optflag("V", "version", "output version information and exit");
|
||||
let matches = App::new(executable!())
|
||||
.version(VERSION)
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.arg(Arg::with_name(OPT_PATH).hidden(true).multiple(true))
|
||||
.get_matches_from(args);
|
||||
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(f) => crash!(1, "invalid options\n{}", f),
|
||||
};
|
||||
let paths: Vec<String> = matches
|
||||
.values_of(OPT_PATH)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
.unwrap_or_default();
|
||||
|
||||
if matches.opt_present("help") {
|
||||
println!("{} {}", NAME, VERSION);
|
||||
println!();
|
||||
println!("Usage:");
|
||||
println!(" {} [FILE]... [OPTION]...", NAME);
|
||||
println!();
|
||||
println!("{}", opts.usage("Unlink the file at [FILE]."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if matches.opt_present("version") {
|
||||
println!("{} {}", NAME, VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if matches.free.is_empty() {
|
||||
if paths.is_empty() {
|
||||
crash!(
|
||||
1,
|
||||
"missing operand\nTry '{0} --help' for more information.",
|
||||
NAME
|
||||
executable!()
|
||||
);
|
||||
} else if matches.free.len() > 1 {
|
||||
} else if paths.len() > 1 {
|
||||
crash!(
|
||||
1,
|
||||
"extra operand: '{1}'\nTry '{0} --help' for more information.",
|
||||
NAME,
|
||||
matches.free[1]
|
||||
executable!(),
|
||||
paths[1]
|
||||
);
|
||||
}
|
||||
|
||||
let c_string = CString::new(matches.free[0].clone()).unwrap(); // unwrap() cannot fail, the string comes from argv so it cannot contain a \0.
|
||||
let c_string = CString::new(paths[0].clone()).unwrap(); // unwrap() cannot fail, the string comes from argv so it cannot contain a \0.
|
||||
|
||||
let st_mode = {
|
||||
#[allow(deprecated)]
|
||||
|
@ -72,12 +66,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
let result = unsafe { lstat(c_string.as_ptr(), &mut buf as *mut stat) };
|
||||
|
||||
if result < 0 {
|
||||
crash!(
|
||||
1,
|
||||
"Cannot stat '{}': {}",
|
||||
matches.free[0],
|
||||
Error::last_os_error()
|
||||
);
|
||||
crash!(1, "Cannot stat '{}': {}", paths[0], Error::last_os_error());
|
||||
}
|
||||
|
||||
buf.st_mode & S_IFMT
|
||||
|
@ -101,7 +90,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
match result {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
crash!(1, "cannot unlink '{0}': {1}", matches.free[0], e);
|
||||
crash!(1, "cannot unlink '{0}': {1}", paths[0], e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,21 @@ use nix::unistd::pipe;
|
|||
|
||||
const BUF_SIZE: usize = 16384;
|
||||
|
||||
/// Splice wrapper which handles short writes
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[inline]
|
||||
fn splice_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> {
|
||||
let mut left = num_bytes;
|
||||
loop {
|
||||
let written = splice(read_fd, None, write_fd, None, left, SpliceFFlags::empty())?;
|
||||
left -= written;
|
||||
if left == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This is a Linux-specific function to count the number of bytes using the
|
||||
/// `splice` system call, which is faster than using `read`.
|
||||
#[inline]
|
||||
|
@ -39,7 +54,7 @@ fn count_bytes_using_splice(fd: RawFd) -> nix::Result<usize> {
|
|||
break;
|
||||
}
|
||||
byte_count += res;
|
||||
splice(pipe_rd, None, null, None, res, SpliceFFlags::empty())?;
|
||||
splice_exact(pipe_rd, null, res)?;
|
||||
}
|
||||
|
||||
Ok(byte_count)
|
||||
|
@ -57,30 +72,27 @@ pub(crate) fn count_bytes_fast<T: WordCountable>(handle: &mut T) -> WcResult<usi
|
|||
#[cfg(unix)]
|
||||
{
|
||||
let fd = handle.as_raw_fd();
|
||||
match fstat(fd) {
|
||||
Ok(stat) => {
|
||||
// If the file is regular, then the `st_size` should hold
|
||||
// the file's size in bytes.
|
||||
if (stat.st_mode & S_IFREG) != 0 {
|
||||
return Ok(stat.st_size as usize);
|
||||
}
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
// Else, if we're on Linux and our file is a FIFO pipe
|
||||
// (or stdin), we use splice to count the number of bytes.
|
||||
if (stat.st_mode & S_IFIFO) != 0 {
|
||||
if let Ok(n) = count_bytes_using_splice(fd) {
|
||||
return Ok(n);
|
||||
}
|
||||
if let Ok(stat) = fstat(fd) {
|
||||
// If the file is regular, then the `st_size` should hold
|
||||
// the file's size in bytes.
|
||||
if (stat.st_mode & S_IFREG) != 0 {
|
||||
return Ok(stat.st_size as usize);
|
||||
}
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
// Else, if we're on Linux and our file is a FIFO pipe
|
||||
// (or stdin), we use splice to count the number of bytes.
|
||||
if (stat.st_mode & S_IFIFO) != 0 {
|
||||
if let Ok(n) = count_bytes_using_splice(fd) {
|
||||
return Ok(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back on `read`, but without the overhead of counting words and lines.
|
||||
let mut buf = [0 as u8; BUF_SIZE];
|
||||
let mut buf = [0_u8; BUF_SIZE];
|
||||
let mut byte_count = 0;
|
||||
loop {
|
||||
match handle.read(&mut buf) {
|
||||
|
|
|
@ -138,11 +138,8 @@ impl AddAssign for WordCount {
|
|||
}
|
||||
|
||||
impl WordCount {
|
||||
fn with_title<'a>(self, title: &'a str) -> TitledWordCount<'a> {
|
||||
return TitledWordCount {
|
||||
title: title,
|
||||
count: self,
|
||||
};
|
||||
fn with_title(self, title: &str) -> TitledWordCount {
|
||||
TitledWordCount { title, count: self }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +248,7 @@ fn is_word_separator(byte: u8) -> bool {
|
|||
fn word_count_from_reader<T: WordCountable>(
|
||||
mut reader: T,
|
||||
settings: &Settings,
|
||||
path: &String,
|
||||
path: &str,
|
||||
) -> WcResult<WordCount> {
|
||||
let only_count_bytes = settings.show_bytes
|
||||
&& (!(settings.show_chars
|
||||
|
@ -333,18 +330,18 @@ fn word_count_from_reader<T: WordCountable>(
|
|||
})
|
||||
}
|
||||
|
||||
fn word_count_from_path(path: &String, settings: &Settings) -> WcResult<WordCount> {
|
||||
fn word_count_from_path(path: &str, settings: &Settings) -> WcResult<WordCount> {
|
||||
if path == "-" {
|
||||
let stdin = io::stdin();
|
||||
let stdin_lock = stdin.lock();
|
||||
return Ok(word_count_from_reader(stdin_lock, settings, path)?);
|
||||
word_count_from_reader(stdin_lock, settings, path)
|
||||
} else {
|
||||
let path_obj = Path::new(path);
|
||||
if path_obj.is_dir() {
|
||||
return Err(WcError::IsDirectory(path.clone()));
|
||||
Err(WcError::IsDirectory(path.to_owned()))
|
||||
} else {
|
||||
let file = File::open(path)?;
|
||||
return Ok(word_count_from_reader(file, settings, path)?);
|
||||
word_count_from_reader(file, settings, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +422,7 @@ fn print_stats(
|
|||
}
|
||||
|
||||
if result.title == "-" {
|
||||
writeln!(stdout_lock, "")?;
|
||||
writeln!(stdout_lock)?;
|
||||
} else {
|
||||
writeln!(stdout_lock, " {}", result.title)?;
|
||||
}
|
||||
|
|
|
@ -222,7 +222,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
need_runlevel,
|
||||
need_users,
|
||||
my_line_only,
|
||||
has_records: false,
|
||||
args: matches.free,
|
||||
};
|
||||
|
||||
|
@ -247,7 +246,6 @@ struct Who {
|
|||
need_runlevel: bool,
|
||||
need_users: bool,
|
||||
my_line_only: bool,
|
||||
has_records: bool,
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
|
@ -321,8 +319,7 @@ impl Who {
|
|||
println!("{}", users.join(" "));
|
||||
println!("# users={}", users.len());
|
||||
} else {
|
||||
let mut records = Utmpx::iter_all_records().read_from(f).peekable();
|
||||
self.has_records = records.peek().is_some();
|
||||
let records = Utmpx::iter_all_records().read_from(f).peekable();
|
||||
|
||||
if self.include_heading {
|
||||
self.print_heading()
|
||||
|
|
|
@ -31,9 +31,9 @@ fn chgrp<P: AsRef<Path>>(path: P, dgid: gid_t, follow: bool) -> IOResult<()> {
|
|||
let s = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||
let ret = unsafe {
|
||||
if follow {
|
||||
libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
|
||||
libc::chown(s.as_ptr(), 0_u32.wrapping_sub(1), dgid)
|
||||
} else {
|
||||
lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
|
||||
lchown(s.as_ptr(), 0_u32.wrapping_sub(1), dgid)
|
||||
}
|
||||
};
|
||||
if ret == 0 {
|
||||
|
|
|
@ -31,6 +31,14 @@ macro_rules! show_error(
|
|||
);
|
||||
|
||||
/// Show a warning to stderr in a silimar style to GNU coreutils.
|
||||
#[macro_export]
|
||||
macro_rules! show_error_custom_description (
|
||||
($err:expr,$($args:tt)+) => ({
|
||||
eprint!("{}: {}: ", executable!(), $err);
|
||||
eprintln!($($args)+);
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! show_warning(
|
||||
($($args:tt)+) => ({
|
||||
|
|
|
@ -2,17 +2,13 @@ use crate::common::util::*;
|
|||
|
||||
#[test]
|
||||
fn test_arch() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.run();
|
||||
assert!(result.success);
|
||||
new_ucmd!().succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arch_help() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("--help").run();
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains("architecture name"));
|
||||
new_ucmd!()
|
||||
.arg("--help")
|
||||
.succeeds()
|
||||
.stdout_contains("architecture name");
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ fn test_zero_param() {
|
|||
}
|
||||
|
||||
fn expect_error(input: Vec<&str>) {
|
||||
assert!(new_ucmd!().args(&input).fails().no_stdout().stderr.len() > 0);
|
||||
assert!(new_ucmd!().args(&input).fails().no_stdout().stderr().len() > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#[cfg(unix)]
|
||||
extern crate unix_socket;
|
||||
|
||||
use crate::common::util::*;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn test_output_simple() {
|
||||
|
@ -11,6 +9,131 @@ fn test_output_simple() {
|
|||
.stdout_only("abcde\nfghij\nklmno\npqrst\nuvwxyz\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_options() {
|
||||
for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
|
||||
// Give fixture through command line file argument
|
||||
new_ucmd!()
|
||||
.args(&[fixture])
|
||||
.succeeds()
|
||||
.stdout_is_fixture(fixture);
|
||||
// Give fixture through stdin
|
||||
new_ucmd!()
|
||||
.pipe_in_fixture(fixture)
|
||||
.succeeds()
|
||||
.stdout_is_fixture(fixture);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_no_options_big_input() {
|
||||
for &n in &[
|
||||
0,
|
||||
1,
|
||||
42,
|
||||
16 * 1024 - 7,
|
||||
16 * 1024 - 1,
|
||||
16 * 1024,
|
||||
16 * 1024 + 1,
|
||||
16 * 1024 + 3,
|
||||
32 * 1024,
|
||||
64 * 1024,
|
||||
80 * 1024,
|
||||
96 * 1024,
|
||||
112 * 1024,
|
||||
128 * 1024,
|
||||
] {
|
||||
let data = vec_of_size(n);
|
||||
let data2 = data.clone();
|
||||
assert_eq!(data.len(), data2.len());
|
||||
new_ucmd!().pipe_in(data).succeeds().stdout_is_bytes(&data2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_fifo_symlink() {
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::thread;
|
||||
|
||||
let s = TestScenario::new(util_name!());
|
||||
s.fixtures.mkdir("dir");
|
||||
s.fixtures.mkfifo("dir/pipe");
|
||||
assert!(s.fixtures.is_fifo("dir/pipe"));
|
||||
|
||||
// Make cat read the pipe through a symlink
|
||||
s.fixtures.symlink_file("dir/pipe", "sympipe");
|
||||
let proc = s.ucmd().args(&["sympipe"]).run_no_wait();
|
||||
|
||||
let data = vec_of_size(128 * 1024);
|
||||
let data2 = data.clone();
|
||||
|
||||
let pipe_path = s.fixtures.plus("dir/pipe");
|
||||
let thread = thread::spawn(move || {
|
||||
let mut pipe = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(false)
|
||||
.open(pipe_path)
|
||||
.unwrap();
|
||||
pipe.write_all(&data).unwrap();
|
||||
});
|
||||
|
||||
let output = proc.wait_with_output().unwrap();
|
||||
assert_eq!(&output.stdout, &data2);
|
||||
thread.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_directory() {
|
||||
let s = TestScenario::new(util_name!());
|
||||
s.fixtures.mkdir("test_directory");
|
||||
s.ucmd()
|
||||
.args(&["test_directory"])
|
||||
.fails()
|
||||
.stderr_is("cat: test_directory: Is a directory");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_directory_and_file() {
|
||||
let s = TestScenario::new(util_name!());
|
||||
s.fixtures.mkdir("test_directory2");
|
||||
for fixture in &["empty.txt", "alpha.txt", "nonewline.txt"] {
|
||||
s.ucmd()
|
||||
.args(&["test_directory2", fixture])
|
||||
.fails()
|
||||
.stderr_is("cat: test_directory2: Is a directory")
|
||||
.stdout_is_fixture(fixture);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_three_directories_and_file_and_stdin() {
|
||||
let s = TestScenario::new(util_name!());
|
||||
s.fixtures.mkdir("test_directory3");
|
||||
s.fixtures.mkdir("test_directory3/test_directory4");
|
||||
s.fixtures.mkdir("test_directory3/test_directory5");
|
||||
s.ucmd()
|
||||
.args(&[
|
||||
"test_directory3/test_directory4",
|
||||
"alpha.txt",
|
||||
"-",
|
||||
"filewhichdoesnotexist.txt",
|
||||
"nonewline.txt",
|
||||
"test_directory3/test_directory5",
|
||||
"test_directory3/../test_directory3/test_directory5",
|
||||
"test_directory3",
|
||||
])
|
||||
.pipe_in("stdout bytes")
|
||||
.fails()
|
||||
.stderr_is_fixture("three_directories_and_file_and_stdin.stderr.expected")
|
||||
.stdout_is(
|
||||
"abcde\nfghij\nklmno\npqrst\nuvwxyz\nstdout bytestext without a trailing newline",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_output_multi_files_print_all_chars() {
|
||||
new_ucmd!()
|
||||
|
@ -149,13 +272,64 @@ fn test_squeeze_blank_before_numbering() {
|
|||
}
|
||||
}
|
||||
|
||||
/// This tests reading from Unix character devices
|
||||
#[test]
|
||||
#[cfg(foo)]
|
||||
#[cfg(unix)]
|
||||
fn test_dev_random() {
|
||||
let mut buf = [0; 2048];
|
||||
let mut proc = new_ucmd!().args(&["/dev/random"]).run_no_wait();
|
||||
let mut proc_stdout = proc.stdout.take().unwrap();
|
||||
proc_stdout.read_exact(&mut buf).unwrap();
|
||||
|
||||
let num_zeroes = buf.iter().fold(0, |mut acc, &n| {
|
||||
if n == 0 {
|
||||
acc += 1;
|
||||
}
|
||||
acc
|
||||
});
|
||||
// The probability of more than 512 zero bytes is essentially zero if the
|
||||
// output is truly random.
|
||||
assert!(num_zeroes < 512);
|
||||
proc.kill().unwrap();
|
||||
}
|
||||
|
||||
/// Reading from /dev/full should return an infinite amount of zero bytes.
|
||||
/// Wikipedia says there is support on Linux, FreeBSD, and NetBSD.
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
|
||||
fn test_dev_full() {
|
||||
let mut buf = [0; 2048];
|
||||
let mut proc = new_ucmd!().args(&["/dev/full"]).run_no_wait();
|
||||
let mut proc_stdout = proc.stdout.take().unwrap();
|
||||
let expected = [0; 2048];
|
||||
proc_stdout.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf[..], &expected[..]);
|
||||
proc.kill().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
|
||||
fn test_dev_full_show_all() {
|
||||
let mut buf = [0; 2048];
|
||||
let mut proc = new_ucmd!().args(&["-A", "/dev/full"]).run_no_wait();
|
||||
let mut proc_stdout = proc.stdout.take().unwrap();
|
||||
proc_stdout.read_exact(&mut buf).unwrap();
|
||||
|
||||
let expected: Vec<u8> = (0..buf.len())
|
||||
.map(|n| if n & 1 == 0 { b'^' } else { b'@' })
|
||||
.collect();
|
||||
|
||||
assert_eq!(&buf[..], &expected[..]);
|
||||
proc.kill().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_domain_socket() {
|
||||
use self::tempdir::TempDir;
|
||||
use self::unix_socket::UnixListener;
|
||||
use std::io::prelude::*;
|
||||
use std::thread;
|
||||
use tempdir::TempDir;
|
||||
use unix_socket::UnixListener;
|
||||
|
||||
let dir = TempDir::new("unix_socket").expect("failed to create dir");
|
||||
let socket_path = dir.path().join("sock");
|
||||
|
|
|
@ -149,7 +149,7 @@ fn test_big_h() {
|
|||
.arg("bin")
|
||||
.arg("/proc/self/fd")
|
||||
.fails()
|
||||
.stderr
|
||||
.stderr_str()
|
||||
.lines()
|
||||
.fold(0, |acc, _| acc + 1)
|
||||
> 1
|
||||
|
|
|
@ -48,7 +48,7 @@ fn run_single_test(test: &TestCase, at: AtPath, mut ucmd: UCommand) {
|
|||
}
|
||||
let r = ucmd.run();
|
||||
if !r.success {
|
||||
println!("{}", r.stderr);
|
||||
println!("{}", r.stderr_str());
|
||||
panic!("{:?}: failed", ucmd.raw);
|
||||
}
|
||||
|
||||
|
@ -297,13 +297,14 @@ fn test_chmod_recursive() {
|
|||
mkfile(&at.plus_as_string("a/b/c/c"), 0o100444);
|
||||
mkfile(&at.plus_as_string("z/y"), 0o100444);
|
||||
|
||||
let result = ucmd
|
||||
.arg("-R")
|
||||
ucmd.arg("-R")
|
||||
.arg("--verbose")
|
||||
.arg("-r,a+w")
|
||||
.arg("a")
|
||||
.arg("z")
|
||||
.succeeds();
|
||||
.succeeds()
|
||||
.stderr_contains(&"to 333 (-wx-wx-wx)")
|
||||
.stderr_contains(&"to 222 (-w--w--w-)");
|
||||
|
||||
assert_eq!(at.metadata("z/y").permissions().mode(), 0o100222);
|
||||
assert_eq!(at.metadata("a/a").permissions().mode(), 0o100222);
|
||||
|
@ -312,8 +313,6 @@ fn test_chmod_recursive() {
|
|||
println!("mode {:o}", at.metadata("a").permissions().mode());
|
||||
assert_eq!(at.metadata("a").permissions().mode(), 0o40333);
|
||||
assert_eq!(at.metadata("z").permissions().mode(), 0o40333);
|
||||
assert!(result.stderr.contains("to 333 (-wx-wx-wx)"));
|
||||
assert!(result.stderr.contains("to 222 (-w--w--w-)"));
|
||||
|
||||
unsafe {
|
||||
umask(original_umask);
|
||||
|
@ -322,30 +321,24 @@ fn test_chmod_recursive() {
|
|||
|
||||
#[test]
|
||||
fn test_chmod_non_existing_file() {
|
||||
let (_at, mut ucmd) = at_and_ucmd!();
|
||||
let result = ucmd
|
||||
new_ucmd!()
|
||||
.arg("-R")
|
||||
.arg("--verbose")
|
||||
.arg("-r,a+w")
|
||||
.arg("dont-exist")
|
||||
.fails();
|
||||
assert!(result
|
||||
.stderr
|
||||
.contains("cannot access 'dont-exist': No such file or directory"));
|
||||
.fails()
|
||||
.stderr_contains(&"cannot access 'dont-exist': No such file or directory");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chmod_preserve_root() {
|
||||
let (_at, mut ucmd) = at_and_ucmd!();
|
||||
let result = ucmd
|
||||
new_ucmd!()
|
||||
.arg("-R")
|
||||
.arg("--preserve-root")
|
||||
.arg("755")
|
||||
.arg("/")
|
||||
.fails();
|
||||
assert!(result
|
||||
.stderr
|
||||
.contains("chmod: error: it is dangerous to operate recursively on '/'"));
|
||||
.fails()
|
||||
.stderr_contains(&"chmod: error: it is dangerous to operate recursively on '/'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -362,33 +355,27 @@ fn test_chmod_symlink_non_existing_file() {
|
|||
let expected_stderr = &format!("cannot operate on dangling symlink '{}'", test_symlink);
|
||||
|
||||
at.symlink_file(non_existing, test_symlink);
|
||||
let mut result;
|
||||
|
||||
// this cannot succeed since the symbolic link dangles
|
||||
result = scene.ucmd().arg("755").arg("-v").arg(test_symlink).fails();
|
||||
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
|
||||
assert!(result.stdout.contains(expected_stdout));
|
||||
assert!(result.stderr.contains(expected_stderr));
|
||||
assert_eq!(result.code, Some(1));
|
||||
scene.ucmd()
|
||||
.arg("755")
|
||||
.arg("-v")
|
||||
.arg(test_symlink)
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stdout_contains(expected_stdout)
|
||||
.stderr_contains(expected_stderr);
|
||||
|
||||
// this should be the same than with just '-v' but without stderr
|
||||
result = scene
|
||||
.ucmd()
|
||||
scene.ucmd()
|
||||
.arg("755")
|
||||
.arg("-v")
|
||||
.arg("-f")
|
||||
.arg(test_symlink)
|
||||
.fails();
|
||||
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
|
||||
assert!(result.stdout.contains(expected_stdout));
|
||||
assert!(result.stderr.is_empty());
|
||||
assert_eq!(result.code, Some(1));
|
||||
.run()
|
||||
.code_is(1)
|
||||
.no_stderr()
|
||||
.stdout_contains(expected_stdout);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -405,18 +392,15 @@ fn test_chmod_symlink_non_existing_file_recursive() {
|
|||
non_existing,
|
||||
&format!("{}/{}", test_directory, test_symlink),
|
||||
);
|
||||
let mut result;
|
||||
|
||||
// this should succeed
|
||||
result = scene
|
||||
.ucmd()
|
||||
scene.ucmd()
|
||||
.arg("-R")
|
||||
.arg("755")
|
||||
.arg(test_directory)
|
||||
.succeeds();
|
||||
assert_eq!(result.code, Some(0));
|
||||
assert!(result.stdout.is_empty());
|
||||
assert!(result.stderr.is_empty());
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.no_stdout();
|
||||
|
||||
let expected_stdout = &format!(
|
||||
"mode of '{}' retained as 0755 (rwxr-xr-x)\nneither symbolic link '{}/{}' nor referent has been changed",
|
||||
|
@ -424,37 +408,25 @@ fn test_chmod_symlink_non_existing_file_recursive() {
|
|||
);
|
||||
|
||||
// '-v': this should succeed without stderr
|
||||
result = scene
|
||||
.ucmd()
|
||||
scene.ucmd()
|
||||
.arg("-R")
|
||||
.arg("-v")
|
||||
.arg("755")
|
||||
.arg(test_directory)
|
||||
.succeeds();
|
||||
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
|
||||
assert!(result.stdout.contains(expected_stdout));
|
||||
assert!(result.stderr.is_empty());
|
||||
assert_eq!(result.code, Some(0));
|
||||
.succeeds()
|
||||
.stdout_contains(expected_stdout)
|
||||
.no_stderr();
|
||||
|
||||
// '-vf': this should be the same than with just '-v'
|
||||
result = scene
|
||||
.ucmd()
|
||||
scene.ucmd()
|
||||
.arg("-R")
|
||||
.arg("-v")
|
||||
.arg("-f")
|
||||
.arg("755")
|
||||
.arg(test_directory)
|
||||
.succeeds();
|
||||
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
|
||||
assert!(result.stdout.contains(expected_stdout));
|
||||
assert!(result.stderr.is_empty());
|
||||
assert_eq!(result.code, Some(0));
|
||||
.succeeds()
|
||||
.stdout_contains(expected_stdout)
|
||||
.no_stderr();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -53,22 +53,22 @@ fn test_chown_myself() {
|
|||
// test chown username file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("results {}", result.stdout);
|
||||
let username = result.stdout.trim_end();
|
||||
println!("results {}", result.stdout_str());
|
||||
let username = result.stdout_str().trim_end();
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let result = ucmd.arg(username).arg(file1).run();
|
||||
println!("results stdout {}", result.stdout);
|
||||
println!("results stderr {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
println!("results stdout {}", result.stdout_str());
|
||||
println!("results stderr {}", result.stderr_str());
|
||||
if is_ci() && result.stderr_str().contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
|
@ -81,24 +81,24 @@ fn test_chown_myself_second() {
|
|||
// test chown username: file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("results {}", result.stdout);
|
||||
println!("results {}", result.stdout_str());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let result = ucmd
|
||||
.arg(result.stdout.trim_end().to_owned() + ":")
|
||||
.arg(result.stdout_str().trim_end().to_owned() + ":")
|
||||
.arg(file1)
|
||||
.run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
|
@ -107,31 +107,31 @@ fn test_chown_myself_group() {
|
|||
// test chown username:group file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("user name = {}", result.stdout);
|
||||
let username = result.stdout.trim_end();
|
||||
println!("user name = {}", result.stdout_str());
|
||||
let username = result.stdout_str().trim_end();
|
||||
|
||||
let result = scene.cmd("id").arg("-gn").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("group name = {}", result.stdout);
|
||||
let group = result.stdout.trim_end();
|
||||
println!("group name = {}", result.stdout_str());
|
||||
let group = result.stdout_str().trim_end();
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
let perm = username.to_owned() + ":" + group;
|
||||
at.touch(file1);
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("chown: invalid group:") {
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
if is_ci() && result.stderr_str().contains("chown: invalid group:") {
|
||||
// With some Ubuntu into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
|
@ -143,27 +143,27 @@ fn test_chown_only_group() {
|
|||
// test chown :group file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("results {}", result.stdout);
|
||||
println!("results {}", result.stdout_str());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
let perm = ":".to_owned() + result.stdout.trim_end();
|
||||
let perm = ":".to_owned() + result.stdout_str().trim_end();
|
||||
at.touch(file1);
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
|
||||
if is_ci() && result.stderr.contains("Operation not permitted") {
|
||||
if is_ci() && result.stderr_str().contains("Operation not permitted") {
|
||||
// With ubuntu with old Rust in the CI, we can get an error
|
||||
return;
|
||||
}
|
||||
if is_ci() && result.stderr.contains("chown: invalid group:") {
|
||||
if is_ci() && result.stderr_str().contains("chown: invalid group:") {
|
||||
// With mac into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
|
@ -174,14 +174,14 @@ fn test_chown_only_group() {
|
|||
fn test_chown_only_id() {
|
||||
// test chown 1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id = String::from(result.stdout.trim());
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let id = String::from(result.stdout_str().trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
@ -189,9 +189,9 @@ fn test_chown_only_id() {
|
|||
at.touch(file1);
|
||||
let result = ucmd.arg(id).arg(file1).run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("chown: invalid user:") {
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
if is_ci() && result.stderr_str().contains("chown: invalid user:") {
|
||||
// With some Ubuntu into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
|
@ -202,14 +202,14 @@ fn test_chown_only_id() {
|
|||
fn test_chown_only_group_id() {
|
||||
// test chown :1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-g").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id = String::from(result.stdout.trim());
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let id = String::from(result.stdout_str().trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
@ -219,9 +219,9 @@ fn test_chown_only_group_id() {
|
|||
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("chown: invalid group:") {
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
if is_ci() && result.stderr_str().contains("chown: invalid group:") {
|
||||
// With mac into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
|
@ -232,24 +232,24 @@ fn test_chown_only_group_id() {
|
|||
fn test_chown_both_id() {
|
||||
// test chown 1111:1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id_user = String::from(result.stdout.trim());
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let id_user = String::from(result.stdout_str().trim());
|
||||
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-g").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id_group = String::from(result.stdout.trim());
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let id_group = String::from(result.stdout_str().trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
@ -258,10 +258,10 @@ fn test_chown_both_id() {
|
|||
let perm = id_user + &":".to_owned() + &id_group;
|
||||
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
if is_ci() && result.stderr_str().contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
|
@ -274,24 +274,24 @@ fn test_chown_both_id() {
|
|||
fn test_chown_both_mix() {
|
||||
// test chown 1111:1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id_user = String::from(result.stdout.trim());
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let id_user = String::from(result.stdout_str().trim());
|
||||
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-gn").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let group_name = String::from(result.stdout.trim());
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let group_name = String::from(result.stdout_str().trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
@ -301,7 +301,7 @@ fn test_chown_both_mix() {
|
|||
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
if is_ci() && result.stderr_str().contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
|
@ -313,14 +313,14 @@ fn test_chown_both_mix() {
|
|||
fn test_chown_recursive() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let username = result.stdout.trim_end();
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let username = result.stdout_str().trim_end();
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.mkdir("a");
|
||||
|
@ -339,31 +339,32 @@ fn test_chown_recursive() {
|
|||
.arg("a")
|
||||
.arg("z")
|
||||
.run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
if is_ci() && result.stderr_str().contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(result.stderr.contains("ownership of 'a/a' retained as"));
|
||||
assert!(result.stderr.contains("ownership of 'z/y' retained as"));
|
||||
assert!(result.success);
|
||||
result
|
||||
.stderr_contains(&"ownership of 'a/a' retained as")
|
||||
.stderr_contains(&"ownership of 'z/y' retained as")
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_preserve() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
if is_ci() && result.stderr_str().contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let username = result.stdout.trim_end();
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let username = result.stdout_str().trim_end();
|
||||
|
||||
let result = new_ucmd!()
|
||||
.arg("--preserve-root")
|
||||
|
@ -371,9 +372,9 @@ fn test_root_preserve() {
|
|||
.arg(username)
|
||||
.arg("/")
|
||||
.fails();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
if is_ci() && result.stderr_str().contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
|
|
|
@ -64,14 +64,14 @@ fn test_preference_of_userspec() {
|
|||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let username = result.stdout.trim_end();
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let username = result.stdout_str().trim_end();
|
||||
|
||||
let ts = TestScenario::new("id");
|
||||
let result = ts.cmd("id").arg("-g").arg("-n").run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
|
||||
if is_ci() && result.stderr.contains("cannot find name for user ID") {
|
||||
// In the CI, some server are failing to return id.
|
||||
|
@ -79,7 +79,7 @@ fn test_preference_of_userspec() {
|
|||
return;
|
||||
}
|
||||
|
||||
let group_name = result.stdout.trim_end();
|
||||
let group_name = result.stdout_str().trim_end();
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
at.mkdir("a");
|
||||
|
@ -93,6 +93,6 @@ fn test_preference_of_userspec() {
|
|||
.arg(format!("--userspec={}:{}", username, group_name))
|
||||
.run();
|
||||
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
}
|
||||
|
|
|
@ -35,14 +35,19 @@ fn test_empty() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_arg_overrides_stdin() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let input = "foobarfoobar";
|
||||
|
||||
at.touch("a");
|
||||
|
||||
let result = ucmd.arg("a").pipe_in(input.as_bytes()).run();
|
||||
let result = ucmd
|
||||
.arg("a")
|
||||
.pipe_in(input.as_bytes())
|
||||
// the command might have exited before all bytes have been pipe in.
|
||||
// in that case, we don't care about the error (broken pipe)
|
||||
.ignore_stdin_write_error()
|
||||
.run();
|
||||
|
||||
println!("{}, {}", result.stdout, result.stderr);
|
||||
|
||||
|
|
|
@ -275,8 +275,8 @@ fn test_cp_arg_no_clobber_twice() {
|
|||
.arg("dest.txt")
|
||||
.run();
|
||||
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
println!("stderr = {:?}", result.stderr_str());
|
||||
println!("stdout = {:?}", result.stdout_str());
|
||||
assert!(result.success);
|
||||
assert!(result.stderr.is_empty());
|
||||
assert_eq!(at.read("source.txt"), "");
|
||||
|
@ -317,8 +317,8 @@ fn test_cp_arg_force() {
|
|||
.arg(TEST_HELLO_WORLD_DEST)
|
||||
.run();
|
||||
|
||||
println!("{:?}", result.stderr);
|
||||
println!("{:?}", result.stdout);
|
||||
println!("{:?}", result.stderr_str());
|
||||
println!("{:?}", result.stdout_str());
|
||||
|
||||
assert!(result.success);
|
||||
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
||||
|
@ -602,7 +602,7 @@ fn test_cp_deref_folder_to_folder() {
|
|||
.arg(TEST_COPY_FROM_FOLDER)
|
||||
.arg(TEST_COPY_TO_FOLDER_NEW)
|
||||
.run();
|
||||
println!("cp output {}", result.stdout);
|
||||
println!("cp output {}", result.stdout_str());
|
||||
|
||||
// Check that the exit code represents a successful copy.
|
||||
assert!(result.success);
|
||||
|
@ -611,12 +611,12 @@ fn test_cp_deref_folder_to_folder() {
|
|||
{
|
||||
let scene2 = TestScenario::new("ls");
|
||||
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
||||
println!("ls source {}", result.stdout);
|
||||
println!("ls source {}", result.stdout_str());
|
||||
|
||||
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
||||
|
||||
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
||||
println!("ls dest {}", result.stdout);
|
||||
println!("ls dest {}", result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -706,7 +706,7 @@ fn test_cp_no_deref_folder_to_folder() {
|
|||
.arg(TEST_COPY_FROM_FOLDER)
|
||||
.arg(TEST_COPY_TO_FOLDER_NEW)
|
||||
.run();
|
||||
println!("cp output {}", result.stdout);
|
||||
println!("cp output {}", result.stdout_str());
|
||||
|
||||
// Check that the exit code represents a successful copy.
|
||||
assert!(result.success);
|
||||
|
@ -715,12 +715,12 @@ fn test_cp_no_deref_folder_to_folder() {
|
|||
{
|
||||
let scene2 = TestScenario::new("ls");
|
||||
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
||||
println!("ls source {}", result.stdout);
|
||||
println!("ls source {}", result.stdout_str());
|
||||
|
||||
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
||||
|
||||
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
||||
println!("ls dest {}", result.stdout);
|
||||
println!("ls dest {}", result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -809,7 +809,7 @@ fn test_cp_archive() {
|
|||
let scene2 = TestScenario::new("ls");
|
||||
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run();
|
||||
|
||||
println!("ls dest {}", result.stdout);
|
||||
println!("ls dest {}", result.stdout_str());
|
||||
assert_eq!(creation, creation2);
|
||||
assert!(result.success);
|
||||
}
|
||||
|
@ -863,7 +863,7 @@ fn test_cp_archive_recursive() {
|
|||
.arg(&at.subdir.join(TEST_COPY_TO_FOLDER))
|
||||
.run();
|
||||
|
||||
println!("ls dest {}", result.stdout);
|
||||
println!("ls dest {}", result.stdout_str());
|
||||
|
||||
let scene2 = TestScenario::new("ls");
|
||||
let result = scene2
|
||||
|
@ -872,7 +872,7 @@ fn test_cp_archive_recursive() {
|
|||
.arg(&at.subdir.join(TEST_COPY_TO_FOLDER_NEW))
|
||||
.run();
|
||||
|
||||
println!("ls dest {}", result.stdout);
|
||||
println!("ls dest {}", result.stdout_str());
|
||||
assert!(at.file_exists(
|
||||
&at.subdir
|
||||
.join(TEST_COPY_TO_FOLDER_NEW)
|
||||
|
@ -946,7 +946,7 @@ fn test_cp_preserve_timestamps() {
|
|||
let scene2 = TestScenario::new("ls");
|
||||
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run();
|
||||
|
||||
println!("ls dest {}", result.stdout);
|
||||
println!("ls dest {}", result.stdout_str());
|
||||
assert_eq!(creation, creation2);
|
||||
assert!(result.success);
|
||||
}
|
||||
|
@ -984,7 +984,7 @@ fn test_cp_dont_preserve_timestamps() {
|
|||
let scene2 = TestScenario::new("ls");
|
||||
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run();
|
||||
|
||||
println!("ls dest {}", result.stdout);
|
||||
println!("ls dest {}", result.stdout_str());
|
||||
println!("creation {:?} / {:?}", creation, creation2);
|
||||
|
||||
assert_ne!(creation, creation2);
|
||||
|
|
|
@ -28,13 +28,13 @@ fn test_date_rfc_3339() {
|
|||
// Check that the output matches the regexp
|
||||
let rfc_regexp = r"(\d+)-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])\s([01]\d|2[0-3]):([0-5]\d):([0-5]\d|60)(\.\d+)?(([Zz])|([\+|\-]([01]\d|2[0-3])))";
|
||||
let re = Regex::new(rfc_regexp).unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
|
||||
result = scene.ucmd().arg("--rfc-3339=seconds").succeeds();
|
||||
|
||||
// Check that the output matches the regexp
|
||||
let re = Regex::new(rfc_regexp).unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -73,13 +73,13 @@ fn test_date_format_y() {
|
|||
|
||||
assert!(result.success);
|
||||
let mut re = Regex::new(r"^\d{4}$").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
|
||||
result = scene.ucmd().arg("+%y").succeeds();
|
||||
|
||||
assert!(result.success);
|
||||
re = Regex::new(r"^\d{2}$").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -90,13 +90,13 @@ fn test_date_format_m() {
|
|||
|
||||
assert!(result.success);
|
||||
let mut re = Regex::new(r"\S+").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
|
||||
result = scene.ucmd().arg("+%m").succeeds();
|
||||
|
||||
assert!(result.success);
|
||||
re = Regex::new(r"^\d{2}$").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -107,20 +107,20 @@ fn test_date_format_day() {
|
|||
|
||||
assert!(result.success);
|
||||
let mut re = Regex::new(r"\S+").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
|
||||
result = scene.ucmd().arg("+%A").succeeds();
|
||||
|
||||
assert!(result.success);
|
||||
|
||||
re = Regex::new(r"\S+").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
|
||||
result = scene.ucmd().arg("+%u").succeeds();
|
||||
|
||||
assert!(result.success);
|
||||
re = Regex::new(r"^\d{1}$").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -131,7 +131,7 @@ fn test_date_format_full_day() {
|
|||
|
||||
assert!(result.success);
|
||||
let re = Regex::new(r"\S+ \d{4}-\d{2}-\d{2}").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str().trim()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -7,10 +7,9 @@ const SUB_LINK: &str = "subdir/links/sublink.txt";
|
|||
|
||||
#[test]
|
||||
fn test_du_basics() {
|
||||
let (_at, mut ucmd) = at_and_ucmd!();
|
||||
let result = ucmd.run();
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stderr, "");
|
||||
new_ucmd!()
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
}
|
||||
#[cfg(target_vendor = "apple")]
|
||||
fn _du_basics(s: String) {
|
||||
|
@ -22,7 +21,7 @@ fn _du_basics(s: String) {
|
|||
assert_eq!(s, answer);
|
||||
}
|
||||
#[cfg(not(target_vendor = "apple"))]
|
||||
fn _du_basics(s: String) {
|
||||
fn _du_basics(s: &str) {
|
||||
let answer = "28\t./subdir
|
||||
8\t./subdir/deeper
|
||||
16\t./subdir/links
|
||||
|
@ -38,19 +37,19 @@ fn test_du_basics_subdir() {
|
|||
let result = ucmd.arg(SUB_DIR).run();
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stderr, "");
|
||||
_du_basics_subdir(result.stdout);
|
||||
_du_basics_subdir(result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
fn _du_basics_subdir(s: String) {
|
||||
fn _du_basics_subdir(s: &str) {
|
||||
assert_eq!(s, "4\tsubdir/deeper\n");
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
fn _du_basics_subdir(s: String) {
|
||||
fn _du_basics_subdir(s: &str) {
|
||||
assert_eq!(s, "0\tsubdir/deeper\n");
|
||||
}
|
||||
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
|
||||
fn _du_basics_subdir(s: String) {
|
||||
fn _du_basics_subdir(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !is_wsl() {
|
||||
assert_eq!(s, "8\tsubdir/deeper\n");
|
||||
|
@ -64,7 +63,7 @@ fn test_du_basics_bad_name() {
|
|||
let (_at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("bad_name").run();
|
||||
assert_eq!(result.stdout, "");
|
||||
assert_eq!(result.stdout_str(), "");
|
||||
assert_eq!(
|
||||
result.stderr,
|
||||
"du: error: bad_name: No such file or directory\n"
|
||||
|
@ -81,20 +80,20 @@ fn test_du_soft_link() {
|
|||
let result = ts.ucmd().arg(SUB_DIR_LINKS).run();
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stderr, "");
|
||||
_du_soft_link(result.stdout);
|
||||
_du_soft_link(result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
fn _du_soft_link(s: String) {
|
||||
fn _du_soft_link(s: &str) {
|
||||
// 'macos' host variants may have `du` output variation for soft links
|
||||
assert!((s == "12\tsubdir/links\n") || (s == "16\tsubdir/links\n"));
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
fn _du_soft_link(s: String) {
|
||||
fn _du_soft_link(s: &str) {
|
||||
assert_eq!(s, "8\tsubdir/links\n");
|
||||
}
|
||||
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
|
||||
fn _du_soft_link(s: String) {
|
||||
fn _du_soft_link(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !is_wsl() {
|
||||
assert_eq!(s, "16\tsubdir/links\n");
|
||||
|
@ -114,19 +113,19 @@ fn test_du_hard_link() {
|
|||
assert!(result.success);
|
||||
assert_eq!(result.stderr, "");
|
||||
// We do not double count hard links as the inodes are identical
|
||||
_du_hard_link(result.stdout);
|
||||
_du_hard_link(result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
fn _du_hard_link(s: String) {
|
||||
fn _du_hard_link(s: &str) {
|
||||
assert_eq!(s, "12\tsubdir/links\n")
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
fn _du_hard_link(s: String) {
|
||||
fn _du_hard_link(s: &str) {
|
||||
assert_eq!(s, "8\tsubdir/links\n")
|
||||
}
|
||||
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
|
||||
fn _du_hard_link(s: String) {
|
||||
fn _du_hard_link(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !is_wsl() {
|
||||
assert_eq!(s, "16\tsubdir/links\n");
|
||||
|
@ -142,19 +141,19 @@ fn test_du_d_flag() {
|
|||
let result = ts.ucmd().arg("-d").arg("1").run();
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stderr, "");
|
||||
_du_d_flag(result.stdout);
|
||||
_du_d_flag(result.stdout_str());
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
fn _du_d_flag(s: String) {
|
||||
fn _du_d_flag(s: &str) {
|
||||
assert_eq!(s, "16\t./subdir\n20\t./\n");
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
fn _du_d_flag(s: String) {
|
||||
fn _du_d_flag(s: &str) {
|
||||
assert_eq!(s, "8\t./subdir\n8\t./\n");
|
||||
}
|
||||
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
|
||||
fn _du_d_flag(s: String) {
|
||||
fn _du_d_flag(s: &str) {
|
||||
// MS-WSL linux has altered expected output
|
||||
if !is_wsl() {
|
||||
assert_eq!(s, "28\t./subdir\n36\t./\n");
|
||||
|
@ -167,10 +166,11 @@ fn _du_d_flag(s: String) {
|
|||
fn test_du_h_flag_empty_file() {
|
||||
let ts = TestScenario::new("du");
|
||||
|
||||
let result = ts.ucmd().arg("-h").arg("empty.txt").run();
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stderr, "");
|
||||
assert_eq!(result.stdout, "0\tempty.txt\n");
|
||||
ts.ucmd()
|
||||
.arg("-h")
|
||||
.arg("empty.txt")
|
||||
.succeeds()
|
||||
.stdout_only("0\tempty.txt\n");
|
||||
}
|
||||
|
||||
#[cfg(feature = "touch")]
|
||||
|
@ -190,3 +190,33 @@ fn test_du_time() {
|
|||
assert_eq!(result.stderr, "");
|
||||
assert_eq!(result.stdout, "0\t2015-05-15 00:00\tdate_test\n");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(feature = "chmod")]
|
||||
#[test]
|
||||
fn test_du_no_permission() {
|
||||
let ts = TestScenario::new("du");
|
||||
|
||||
let chmod = ts.ccmd("chmod").arg("-r").arg(SUB_DIR_LINKS).run();
|
||||
println!("chmod output: {:?}", chmod);
|
||||
assert!(chmod.success);
|
||||
let result = ts.ucmd().arg(SUB_DIR_LINKS).run();
|
||||
|
||||
ts.ccmd("chmod").arg("+r").arg(SUB_DIR_LINKS).run();
|
||||
|
||||
assert!(result.success);
|
||||
assert_eq!(
|
||||
result.stderr,
|
||||
"du: cannot read directory ‘subdir/links‘: Permission denied (os error 13)\n"
|
||||
);
|
||||
_du_no_permission(result.stdout);
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
fn _du_no_permission(s: String) {
|
||||
assert_eq!(s, "0\tsubdir/links\n");
|
||||
}
|
||||
#[cfg(all(not(target_vendor = "apple"), not(target_os = "windows")))]
|
||||
fn _du_no_permission(s: String) {
|
||||
assert_eq!(s, "4\tsubdir/links\n");
|
||||
}
|
||||
|
|
|
@ -2,22 +2,20 @@ use crate::common::util::*;
|
|||
|
||||
#[test]
|
||||
fn test_default() {
|
||||
//CmdResult.stdout_only(...) trims trailing newlines
|
||||
assert_eq!("hi\n", new_ucmd!().arg("hi").succeeds().no_stderr().stdout);
|
||||
new_ucmd!()
|
||||
.arg("hi")
|
||||
.succeeds()
|
||||
.stdout_only("hi\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_trailing_newline() {
|
||||
//CmdResult.stdout_only(...) trims trailing newlines
|
||||
assert_eq!(
|
||||
"hi",
|
||||
new_ucmd!()
|
||||
.arg("-n")
|
||||
.arg("hi")
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout
|
||||
);
|
||||
new_ucmd!()
|
||||
.arg("-n")
|
||||
.arg("hi")
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout_only("hi");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -192,39 +190,38 @@ fn test_hyphen_values_inside_string() {
|
|||
new_ucmd!()
|
||||
.arg("'\"\n'CXXFLAGS=-g -O2'\n\"'")
|
||||
.succeeds()
|
||||
.stdout
|
||||
.contains("CXXFLAGS");
|
||||
.stdout_contains("CXXFLAGS");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hyphen_values_at_start() {
|
||||
let result = new_ucmd!()
|
||||
new_ucmd!()
|
||||
.arg("-E")
|
||||
.arg("-test")
|
||||
.arg("araba")
|
||||
.arg("-merci")
|
||||
.run();
|
||||
|
||||
assert!(result.success);
|
||||
assert_eq!(false, result.stdout.contains("-E"));
|
||||
assert_eq!(result.stdout, "-test araba -merci\n");
|
||||
.run()
|
||||
.success()
|
||||
.stdout_does_not_contain("-E")
|
||||
.stdout_is("-test araba -merci\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hyphen_values_between() {
|
||||
let result = new_ucmd!().arg("test").arg("-E").arg("araba").run();
|
||||
new_ucmd!()
|
||||
.arg("test")
|
||||
.arg("-E")
|
||||
.arg("araba")
|
||||
.run()
|
||||
.success()
|
||||
.stdout_is("test -E araba\n");
|
||||
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stdout, "test -E araba\n");
|
||||
|
||||
let result = new_ucmd!()
|
||||
new_ucmd!()
|
||||
.arg("dumdum ")
|
||||
.arg("dum dum dum")
|
||||
.arg("-e")
|
||||
.arg("dum")
|
||||
.run();
|
||||
|
||||
assert!(result.success);
|
||||
assert_eq!(result.stdout, "dumdum dum dum dum -e dum\n");
|
||||
assert_eq!(true, result.stdout.contains("-e"));
|
||||
.run()
|
||||
.success()
|
||||
.stdout_is("dumdum dum dum dum -e dum\n");
|
||||
}
|
||||
|
|
|
@ -8,45 +8,35 @@ use tempfile::tempdir;
|
|||
|
||||
#[test]
|
||||
fn test_env_help() {
|
||||
assert!(new_ucmd!()
|
||||
new_ucmd!()
|
||||
.arg("--help")
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout
|
||||
.contains("OPTIONS:"));
|
||||
.stdout_contains("OPTIONS:");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_version() {
|
||||
assert!(new_ucmd!()
|
||||
new_ucmd!()
|
||||
.arg("--version")
|
||||
.succeeds()
|
||||
.no_stderr()
|
||||
.stdout
|
||||
.contains(util_name!()));
|
||||
.stdout_contains(util_name!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_echo() {
|
||||
// assert!(new_ucmd!().arg("printf").arg("FOO-bar").succeeds().no_stderr().stdout.contains("FOO-bar"));
|
||||
let mut cmd = new_ucmd!();
|
||||
cmd.arg("echo").arg("FOO-bar");
|
||||
println!("cmd={:?}", cmd);
|
||||
let result = new_ucmd!()
|
||||
.arg("echo")
|
||||
.arg("FOO-bar")
|
||||
.succeeds();
|
||||
|
||||
let result = cmd.run();
|
||||
println!("success={:?}", result.success);
|
||||
println!("stdout={:?}", result.stdout);
|
||||
println!("stderr={:?}", result.stderr);
|
||||
assert!(result.success);
|
||||
|
||||
let out = result.stdout.trim_end();
|
||||
|
||||
assert_eq!(out, "FOO-bar");
|
||||
assert_eq!(result.stdout_str().trim(), "FOO-bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_option() {
|
||||
let out = new_ucmd!().arg("-f").arg("vars.conf.txt").run().stdout;
|
||||
let out = new_ucmd!().arg("-f").arg("vars.conf.txt").run().stdout_move_str();
|
||||
|
||||
assert_eq!(
|
||||
out.lines()
|
||||
|
@ -63,7 +53,7 @@ fn test_combined_file_set() {
|
|||
.arg("vars.conf.txt")
|
||||
.arg("FOO=bar.alt")
|
||||
.run()
|
||||
.stdout;
|
||||
.stdout_move_str();
|
||||
|
||||
assert_eq!(out.lines().filter(|&line| line == "FOO=bar.alt").count(), 1);
|
||||
}
|
||||
|
@ -76,8 +66,8 @@ fn test_combined_file_set_unset() {
|
|||
.arg("-f")
|
||||
.arg("vars.conf.txt")
|
||||
.arg("FOO=bar.alt")
|
||||
.run()
|
||||
.stdout;
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
|
||||
assert_eq!(
|
||||
out.lines()
|
||||
|
@ -89,17 +79,17 @@ fn test_combined_file_set_unset() {
|
|||
|
||||
#[test]
|
||||
fn test_single_name_value_pair() {
|
||||
let out = new_ucmd!().arg("FOO=bar").run().stdout;
|
||||
let out = new_ucmd!().arg("FOO=bar").run();
|
||||
|
||||
assert!(out.lines().any(|line| line == "FOO=bar"));
|
||||
assert!(out.stdout_str().lines().any(|line| line == "FOO=bar"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_name_value_pairs() {
|
||||
let out = new_ucmd!().arg("FOO=bar").arg("ABC=xyz").run().stdout;
|
||||
let out = new_ucmd!().arg("FOO=bar").arg("ABC=xyz").run();
|
||||
|
||||
assert_eq!(
|
||||
out.lines()
|
||||
out.stdout_str().lines()
|
||||
.filter(|&line| line == "FOO=bar" || line == "ABC=xyz")
|
||||
.count(),
|
||||
2
|
||||
|
@ -110,13 +100,8 @@ fn test_multiple_name_value_pairs() {
|
|||
fn test_ignore_environment() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let out = scene.ucmd().arg("-i").run().stdout;
|
||||
|
||||
assert_eq!(out, "");
|
||||
|
||||
let out = scene.ucmd().arg("-").run().stdout;
|
||||
|
||||
assert_eq!(out, "");
|
||||
scene.ucmd().arg("-i").run().no_stdout();
|
||||
scene.ucmd().arg("-").run().no_stdout();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -126,8 +111,8 @@ fn test_null_delimiter() {
|
|||
.arg("--null")
|
||||
.arg("FOO=bar")
|
||||
.arg("ABC=xyz")
|
||||
.run()
|
||||
.stdout;
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
|
||||
let mut vars: Vec<_> = out.split('\0').collect();
|
||||
assert_eq!(vars.len(), 3);
|
||||
|
@ -145,8 +130,8 @@ fn test_unset_variable() {
|
|||
.ucmd_keepenv()
|
||||
.arg("-u")
|
||||
.arg("HOME")
|
||||
.run()
|
||||
.stdout;
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
|
||||
assert_eq!(out.lines().any(|line| line.starts_with("HOME=")), false);
|
||||
}
|
||||
|
@ -173,8 +158,8 @@ fn test_change_directory() {
|
|||
.arg("--chdir")
|
||||
.arg(&temporary_path)
|
||||
.arg(pwd)
|
||||
.run()
|
||||
.stdout;
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
assert_eq!(out.trim(), temporary_path.as_os_str())
|
||||
}
|
||||
|
||||
|
@ -193,8 +178,8 @@ fn test_change_directory() {
|
|||
.ucmd()
|
||||
.arg("--chdir")
|
||||
.arg(&temporary_path)
|
||||
.run()
|
||||
.stdout;
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
assert_eq!(
|
||||
out.lines()
|
||||
.any(|line| line.ends_with(temporary_path.file_name().unwrap().to_str().unwrap())),
|
||||
|
@ -214,6 +199,6 @@ fn test_fail_change_directory() {
|
|||
.arg(some_non_existing_path)
|
||||
.arg("pwd")
|
||||
.fails()
|
||||
.stderr;
|
||||
.stderr_move_str();
|
||||
assert!(out.contains("env: cannot change directory to "));
|
||||
}
|
||||
|
|
|
@ -2,57 +2,54 @@ use crate::common::util::*;
|
|||
|
||||
#[test]
|
||||
fn test_with_tab() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("with-tab.txt").run();
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains(" "));
|
||||
assert!(!result.stdout.contains("\t"));
|
||||
new_ucmd!()
|
||||
.arg("with-tab.txt")
|
||||
.succeeds()
|
||||
.stdout_contains(" ")
|
||||
.stdout_does_not_contain("\t");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_trailing_tab() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("with-trailing-tab.txt").run();
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains("with tabs=> "));
|
||||
assert!(!result.stdout.contains("\t"));
|
||||
new_ucmd!()
|
||||
.arg("with-trailing-tab.txt")
|
||||
.succeeds()
|
||||
.stdout_contains("with tabs=> ")
|
||||
.stdout_does_not_contain("\t");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_trailing_tab_i() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("with-trailing-tab.txt").arg("-i").run();
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains(" // with tabs=>\t"));
|
||||
new_ucmd!()
|
||||
.arg("with-trailing-tab.txt")
|
||||
.arg("-i")
|
||||
.succeeds()
|
||||
.stdout_contains(" // with tabs=>\t");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_tab_size() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("with-tab.txt").arg("--tabs=10").run();
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains(" "));
|
||||
new_ucmd!()
|
||||
.arg("with-tab.txt")
|
||||
.arg("--tabs=10")
|
||||
.succeeds()
|
||||
.stdout_contains(" ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_space() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("with-spaces.txt").run();
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains(" return"));
|
||||
new_ucmd!()
|
||||
.arg("with-spaces.txt")
|
||||
.succeeds()
|
||||
.stdout_contains(" return");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_multiple_files() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.arg("with-spaces.txt").arg("with-tab.txt").run();
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains(" return"));
|
||||
assert!(result.stdout.contains(" "));
|
||||
new_ucmd!()
|
||||
.arg("with-spaces.txt")
|
||||
.arg("with-tab.txt")
|
||||
.succeeds()
|
||||
.stdout_contains(" return")
|
||||
.stdout_contains(" ");
|
||||
}
|
||||
|
|
|
@ -32,13 +32,10 @@ fn test_first_100000_integers() {
|
|||
}
|
||||
|
||||
println!("STDIN='{}'", instring);
|
||||
let result = new_ucmd!().pipe_in(instring.as_bytes()).run();
|
||||
let stdout = result.stdout;
|
||||
|
||||
assert!(result.success);
|
||||
let result = new_ucmd!().pipe_in(instring.as_bytes()).succeeds();
|
||||
|
||||
// `seq 0 100000 | factor | sha1sum` => "4ed2d8403934fa1c76fe4b84c5d4b8850299c359"
|
||||
let hash_check = sha1::Sha1::from(stdout.as_bytes()).hexdigest();
|
||||
let hash_check = sha1::Sha1::from(result.stdout()).hexdigest();
|
||||
assert_eq!(hash_check, "4ed2d8403934fa1c76fe4b84c5d4b8850299c359");
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ fn test_fmt() {
|
|||
let result = new_ucmd!().arg("one-word-per-line.txt").run();
|
||||
//.stdout_is_fixture("call_graph.expected");
|
||||
assert_eq!(
|
||||
result.stdout.trim(),
|
||||
result.stdout_str().trim(),
|
||||
"this is a file with one word per line"
|
||||
);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ fn test_fmt_q() {
|
|||
let result = new_ucmd!().arg("-q").arg("one-word-per-line.txt").run();
|
||||
//.stdout_is_fixture("call_graph.expected");
|
||||
assert_eq!(
|
||||
result.stdout.trim(),
|
||||
result.stdout_str().trim(),
|
||||
"this is a file with one word per line"
|
||||
);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ fn test_fmt_w() {
|
|||
.arg("one-word-per-line.txt")
|
||||
.run();
|
||||
//.stdout_is_fixture("call_graph.expected");
|
||||
assert_eq!(result.stdout.trim(), "this is a file with one word per line");
|
||||
assert_eq!(result.stdout_str().trim(), "this is a file with one word per line");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,26 +2,25 @@ use crate::common::util::*;
|
|||
|
||||
#[test]
|
||||
fn test_groups() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
let result = ucmd.run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stdout.trim().is_empty() {
|
||||
let result = new_ucmd!().run();
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
if is_ci() && result.stdout_str().trim().is_empty() {
|
||||
// In the CI, some server are failing to return the group.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
assert!(!result.stdout.trim().is_empty());
|
||||
assert!(!result.stdout_str().trim().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_groups_arg() {
|
||||
// get the username with the "id -un" command
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-un").run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let s1 = String::from(result.stdout.trim());
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
let s1 = String::from(result.stdout_str().trim());
|
||||
if is_ci() && s1.parse::<f64>().is_ok() {
|
||||
// In the CI, some server are failing to return id -un.
|
||||
// So, if we are getting a uid, just skip this test
|
||||
|
@ -29,18 +28,18 @@ fn test_groups_arg() {
|
|||
return;
|
||||
}
|
||||
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
assert!(result.success);
|
||||
assert!(!result.stdout.is_empty());
|
||||
let username = result.stdout.trim();
|
||||
assert!(!result.stdout_str().is_empty());
|
||||
let username = result.stdout_str().trim();
|
||||
|
||||
// call groups with the user name to check that we
|
||||
// are getting something
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
let result = ucmd.arg(username).run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
println!("result.stdout = {}", result.stdout_str());
|
||||
println!("result.stderr = {}", result.stderr_str());
|
||||
assert!(result.success);
|
||||
assert!(!result.stdout.is_empty());
|
||||
assert!(!result.stdout_str().is_empty());
|
||||
}
|
||||
|
|
|
@ -17,14 +17,14 @@ macro_rules! test_digest {
|
|||
fn test_single_file() {
|
||||
let ts = TestScenario::new("hashsum");
|
||||
assert_eq!(ts.fixtures.read(EXPECTED_FILE),
|
||||
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("input.txt").succeeds().no_stderr().stdout));
|
||||
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("input.txt").succeeds().no_stderr().stdout_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin() {
|
||||
let ts = TestScenario::new("hashsum");
|
||||
assert_eq!(ts.fixtures.read(EXPECTED_FILE),
|
||||
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture("input.txt").succeeds().no_stderr().stdout));
|
||||
get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture("input.txt").succeeds().no_stderr().stdout_str()));
|
||||
}
|
||||
}
|
||||
)*)
|
||||
|
|
|
@ -9,5 +9,5 @@ fn test_normal() {
|
|||
|
||||
assert!(result.success);
|
||||
let re = Regex::new(r"^[0-9a-f]{8}").unwrap();
|
||||
assert!(re.is_match(&result.stdout.trim()));
|
||||
assert!(re.is_match(&result.stdout_str()));
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ fn test_hostname() {
|
|||
let ls_short_res = new_ucmd!().arg("-s").succeeds();
|
||||
let ls_domain_res = new_ucmd!().arg("-d").succeeds();
|
||||
|
||||
assert!(ls_default_res.stdout.len() >= ls_short_res.stdout.len());
|
||||
assert!(ls_default_res.stdout.len() >= ls_domain_res.stdout.len());
|
||||
assert!(ls_default_res.stdout().len() >= ls_short_res.stdout().len());
|
||||
assert!(ls_default_res.stdout().len() >= ls_domain_res.stdout().len());
|
||||
}
|
||||
|
||||
// FixME: fails for "MacOS"
|
||||
|
@ -17,14 +17,14 @@ fn test_hostname_ip() {
|
|||
let result = new_ucmd!().arg("-i").run();
|
||||
println!("{:#?}", result);
|
||||
assert!(result.success);
|
||||
assert!(!result.stdout.trim().is_empty());
|
||||
assert!(!result.stdout_str().trim().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hostname_full() {
|
||||
let result = new_ucmd!().arg("-f").succeeds();
|
||||
assert!(!result.stdout.trim().is_empty());
|
||||
|
||||
let ls_short_res = new_ucmd!().arg("-s").succeeds();
|
||||
assert!(result.stdout.trim().contains(ls_short_res.stdout.trim()));
|
||||
assert!(!ls_short_res.stdout_str().trim().is_empty());
|
||||
|
||||
new_ucmd!().arg("-f").succeeds()
|
||||
.stdout_contains(ls_short_res.stdout_str().trim());
|
||||
}
|
||||
|
|
|
@ -9,33 +9,29 @@ fn return_whoami_username() -> String {
|
|||
return String::from("");
|
||||
}
|
||||
|
||||
result.stdout.trim().to_string()
|
||||
result.stdout_str().trim().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let mut result = scene.ucmd().arg("-u").run();
|
||||
let result = new_ucmd!().arg("-u").run();
|
||||
if result.stderr.contains("cannot find name for user ID") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
|
||||
let uid = String::from(result.stdout.trim());
|
||||
result = scene.ucmd().run();
|
||||
let uid = result.success().stdout_str().trim();
|
||||
let result = new_ucmd!().run();
|
||||
if is_ci() && result.stderr.contains("cannot find name for user ID") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if !result.stderr.contains("Could not find uid") {
|
||||
|
||||
if !result.stderr_str().contains("Could not find uid") {
|
||||
// Verify that the id found by --user/-u exists in the list
|
||||
assert!(result.stdout.contains(&uid));
|
||||
result.success().stdout_contains(&uid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,88 +43,64 @@ fn test_id_from_name() {
|
|||
return;
|
||||
}
|
||||
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.ucmd().arg(&username).succeeds();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
let uid = String::from(result.stdout.trim());
|
||||
let result = scene.ucmd().succeeds();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
// Verify that the id found by --user/-u exists in the list
|
||||
assert!(result.stdout.contains(&uid));
|
||||
// Verify that the username found by whoami exists in the list
|
||||
assert!(result.stdout.contains(&username));
|
||||
let result = new_ucmd!().arg(&username).succeeds();
|
||||
let uid = result.stdout_str().trim();
|
||||
|
||||
new_ucmd!().succeeds()
|
||||
// Verify that the id found by --user/-u exists in the list
|
||||
.stdout_contains(uid)
|
||||
// Verify that the username found by whoami exists in the list
|
||||
.stdout_contains(username);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_name_from_id() {
|
||||
let mut scene = TestScenario::new(util_name!());
|
||||
let result = scene.ucmd().arg("-u").run();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
let uid = String::from(result.stdout.trim());
|
||||
let result = new_ucmd!().arg("-u").succeeds();
|
||||
let uid = result.stdout_str().trim();
|
||||
|
||||
scene = TestScenario::new(util_name!());
|
||||
let result = scene.ucmd().arg("-nu").arg(uid).run();
|
||||
let result = new_ucmd!().arg("-nu").arg(uid).run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
|
||||
let username_id = String::from(result.stdout.trim());
|
||||
let username_id = result
|
||||
.success()
|
||||
.stdout_str()
|
||||
.trim();
|
||||
|
||||
scene = TestScenario::new("whoami");
|
||||
let result = scene.cmd("whoami").run();
|
||||
let scene = TestScenario::new("whoami");
|
||||
let result = scene.cmd("whoami").succeeds();
|
||||
|
||||
let username_whoami = result.stdout.trim();
|
||||
let username_whoami = result.stdout_str().trim();
|
||||
|
||||
assert_eq!(username_id, username_whoami);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_group() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let mut result = scene.ucmd().arg("-g").succeeds();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
let s1 = String::from(result.stdout.trim());
|
||||
let mut result = new_ucmd!().arg("-g").succeeds();
|
||||
let s1 = result.stdout_str().trim();
|
||||
assert!(s1.parse::<f64>().is_ok());
|
||||
|
||||
result = scene.ucmd().arg("--group").succeeds();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
let s1 = String::from(result.stdout.trim());
|
||||
result = new_ucmd!().arg("--group").succeeds();
|
||||
let s1 = result.stdout_str().trim();
|
||||
assert!(s1.parse::<f64>().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_groups() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let result = scene.ucmd().arg("-G").succeeds();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let result = new_ucmd!().arg("-G").succeeds();
|
||||
assert!(result.success);
|
||||
let groups = result.stdout.trim().split_whitespace();
|
||||
let groups = result.stdout_str().trim().split_whitespace();
|
||||
for s in groups {
|
||||
assert!(s.parse::<f64>().is_ok());
|
||||
}
|
||||
|
||||
let result = scene.ucmd().arg("--groups").succeeds();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let result = new_ucmd!().arg("--groups").succeeds();
|
||||
assert!(result.success);
|
||||
let groups = result.stdout.trim().split_whitespace();
|
||||
let groups = result.stdout_str().trim().split_whitespace();
|
||||
for s in groups {
|
||||
assert!(s.parse::<f64>().is_ok());
|
||||
}
|
||||
|
@ -136,15 +108,12 @@ fn test_id_groups() {
|
|||
|
||||
#[test]
|
||||
fn test_id_user() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let mut result = scene.ucmd().arg("-u").succeeds();
|
||||
assert!(result.success);
|
||||
let s1 = String::from(result.stdout.trim());
|
||||
let mut result = new_ucmd!().arg("-u").succeeds();
|
||||
let s1 = result.stdout_str().trim();
|
||||
assert!(s1.parse::<f64>().is_ok());
|
||||
result = scene.ucmd().arg("--user").succeeds();
|
||||
assert!(result.success);
|
||||
let s1 = String::from(result.stdout.trim());
|
||||
|
||||
result = new_ucmd!().arg("--user").succeeds();
|
||||
let s1 = result.stdout_str().trim();
|
||||
assert!(s1.parse::<f64>().is_ok());
|
||||
}
|
||||
|
||||
|
@ -156,17 +125,13 @@ fn test_id_pretty_print() {
|
|||
return;
|
||||
}
|
||||
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.ucmd().arg("-p").run();
|
||||
if result.stdout.trim() == "" {
|
||||
let result = new_ucmd!().arg("-p").run();
|
||||
if result.stdout_str().trim() == "" {
|
||||
// Sometimes, the CI is failing here with
|
||||
// old rust versions on Linux
|
||||
return;
|
||||
}
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.contains(&username));
|
||||
result.success().stdout_contains(username);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -176,12 +141,7 @@ fn test_id_password_style() {
|
|||
// Sometimes, the CI is failing here
|
||||
return;
|
||||
}
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let result = scene.ucmd().arg("-P").succeeds();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
assert!(result.stdout.starts_with(&username));
|
||||
let result = new_ucmd!().arg("-P").succeeds();
|
||||
assert!(result.stdout_str().starts_with(&username));
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use crate::common::util::*;
|
|||
use filetime::FileTime;
|
||||
use rust_users::*;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
#[cfg(not(windows))]
|
||||
use std::process::Command;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::thread::sleep;
|
||||
|
||||
|
@ -193,12 +195,8 @@ fn test_install_mode_numeric() {
|
|||
let mode_arg = "-m 0333";
|
||||
at.mkdir(dir2);
|
||||
|
||||
let result = scene.ucmd().arg(mode_arg).arg(file).arg(dir2).run();
|
||||
scene.ucmd().arg(mode_arg).arg(file).arg(dir2).succeeds();
|
||||
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
|
||||
assert!(result.success);
|
||||
let dest_file = &format!("{}/{}", dir2, file);
|
||||
assert!(at.file_exists(file));
|
||||
assert!(at.file_exists(dest_file));
|
||||
|
@ -311,16 +309,13 @@ fn test_install_target_new_file_with_group() {
|
|||
.arg(format!("{}/{}", dir, file))
|
||||
.run();
|
||||
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
|
||||
if is_ci() && result.stderr.contains("error: no such group:") {
|
||||
if is_ci() && result.stderr_str().contains("error: no such group:") {
|
||||
// In the CI, some server are failing to return the group.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(result.success);
|
||||
result.success();
|
||||
assert!(at.file_exists(file));
|
||||
assert!(at.file_exists(&format!("{}/{}", dir, file)));
|
||||
}
|
||||
|
@ -341,16 +336,13 @@ fn test_install_target_new_file_with_owner() {
|
|||
.arg(format!("{}/{}", dir, file))
|
||||
.run();
|
||||
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
|
||||
if is_ci() && result.stderr.contains("error: no such user:") {
|
||||
// In the CI, some server are failing to return the user id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(result.success);
|
||||
result.success();
|
||||
assert!(at.file_exists(file));
|
||||
assert!(at.file_exists(&format!("{}/{}", dir, file)));
|
||||
}
|
||||
|
@ -364,13 +356,10 @@ fn test_install_target_new_file_failing_nonexistent_parent() {
|
|||
|
||||
at.touch(file1);
|
||||
|
||||
let err = ucmd
|
||||
.arg(file1)
|
||||
ucmd.arg(file1)
|
||||
.arg(format!("{}/{}", dir, file2))
|
||||
.fails()
|
||||
.stderr;
|
||||
|
||||
assert!(err.contains("not a directory"))
|
||||
.stderr_contains(&"not a directory");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -415,18 +404,12 @@ fn test_install_copy_file() {
|
|||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_install_target_file_dev_null() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let file1 = "/dev/null";
|
||||
let file2 = "target_file";
|
||||
|
||||
let result = scene.ucmd().arg(file1).arg(file2).run();
|
||||
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
|
||||
assert!(result.success);
|
||||
ucmd.arg(file1).arg(file2).succeeds();
|
||||
|
||||
assert!(at.file_exists(file2));
|
||||
}
|
||||
|
@ -566,3 +549,97 @@ fn test_install_copy_then_compare_file_with_extra_mode() {
|
|||
|
||||
assert!(after_install_sticky != after_install_sticky_again);
|
||||
}
|
||||
|
||||
const STRIP_TARGET_FILE: &str = "helloworld_installed";
|
||||
const SYMBOL_DUMP_PROGRAM: &str = "objdump";
|
||||
const STRIP_SOURCE_FILE_SYMBOL: &str = "main";
|
||||
|
||||
fn strip_source_file() -> &'static str {
|
||||
if cfg!(target_os = "macos") {
|
||||
"helloworld_macos"
|
||||
} else {
|
||||
"helloworld_linux"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_install_and_strip() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-s")
|
||||
.arg(strip_source_file())
|
||||
.arg(STRIP_TARGET_FILE)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
let output = Command::new(SYMBOL_DUMP_PROGRAM)
|
||||
.arg("-t")
|
||||
.arg(at.plus(STRIP_TARGET_FILE))
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(!stdout.contains(STRIP_SOURCE_FILE_SYMBOL));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_install_and_strip_with_program() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-s")
|
||||
.arg("--strip-program")
|
||||
.arg("/usr/bin/strip")
|
||||
.arg(strip_source_file())
|
||||
.arg(STRIP_TARGET_FILE)
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
let output = Command::new(SYMBOL_DUMP_PROGRAM)
|
||||
.arg("-t")
|
||||
.arg(at.plus(STRIP_TARGET_FILE))
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
assert!(!stdout.contains(STRIP_SOURCE_FILE_SYMBOL));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_install_and_strip_with_invalid_program() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let stderr = scene
|
||||
.ucmd()
|
||||
.arg("-s")
|
||||
.arg("--strip-program")
|
||||
.arg("/bin/date")
|
||||
.arg(strip_source_file())
|
||||
.arg(STRIP_TARGET_FILE)
|
||||
.fails()
|
||||
.stderr;
|
||||
assert!(stderr.contains("strip program failed"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_install_and_strip_with_non_existent_program() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
||||
let stderr = scene
|
||||
.ucmd()
|
||||
.arg("-s")
|
||||
.arg("--strip-program")
|
||||
.arg("/usr/bin/non_existent_program")
|
||||
.arg(strip_source_file())
|
||||
.arg(STRIP_TARGET_FILE)
|
||||
.fails()
|
||||
.stderr;
|
||||
assert!(stderr.contains("No such file or directory"));
|
||||
}
|
||||
|
|
|
@ -520,10 +520,7 @@ fn test_symlink_no_deref_dir() {
|
|||
scene.ucmd().args(&["-sn", dir1, link]).fails();
|
||||
|
||||
// Try with the no-deref
|
||||
let result = scene.ucmd().args(&["-sfn", dir1, link]).run();
|
||||
println!("stdout {}", result.stdout);
|
||||
println!("stderr {}", result.stderr);
|
||||
assert!(result.success);
|
||||
scene.ucmd().args(&["-sfn", dir1, link]).succeeds();
|
||||
assert!(at.dir_exists(dir1));
|
||||
assert!(at.dir_exists(dir2));
|
||||
assert!(at.is_symlink(link));
|
||||
|
@ -566,10 +563,7 @@ fn test_symlink_no_deref_file() {
|
|||
scene.ucmd().args(&["-sn", file1, link]).fails();
|
||||
|
||||
// Try with the no-deref
|
||||
let result = scene.ucmd().args(&["-sfn", file1, link]).run();
|
||||
println!("stdout {}", result.stdout);
|
||||
println!("stderr {}", result.stderr);
|
||||
assert!(result.success);
|
||||
scene.ucmd().args(&["-sfn", file1, link]).succeeds();
|
||||
assert!(at.file_exists(file1));
|
||||
assert!(at.file_exists(file2));
|
||||
assert!(at.is_symlink(link));
|
||||
|
|
|
@ -3,23 +3,19 @@ use std::env;
|
|||
|
||||
#[test]
|
||||
fn test_normal() {
|
||||
let (_, mut ucmd) = at_and_ucmd!();
|
||||
|
||||
let result = ucmd.run();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let result = new_ucmd!().run();
|
||||
println!("env::var(CI).is_ok() = {}", env::var("CI").is_ok());
|
||||
|
||||
for (key, value) in env::vars() {
|
||||
println!("{}: {}", key, value);
|
||||
}
|
||||
if (is_ci() || is_wsl()) && result.stderr.contains("error: no login name") {
|
||||
if (is_ci() || is_wsl()) && result.stderr_str().contains("error: no login name") {
|
||||
// ToDO: investigate WSL failure
|
||||
// In the CI, some server are failing to return logname.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(result.success);
|
||||
assert!(!result.stdout.trim().is_empty());
|
||||
result.success();
|
||||
assert!(!result.stdout_str().trim().is_empty());
|
||||
}
|
||||
|
|
|
@ -1 +1,51 @@
|
|||
// ToDO: add tests
|
||||
use crate::common::util::*;
|
||||
|
||||
#[test]
|
||||
fn test_shred_remove() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file_a = "test_shred_remove_a";
|
||||
let file_b = "test_shred_remove_b";
|
||||
|
||||
// Create file_a and file_b.
|
||||
at.touch(file_a);
|
||||
at.touch(file_b);
|
||||
|
||||
// Shred file_a.
|
||||
scene.ucmd().arg("-u").arg(file_a).run();
|
||||
|
||||
// file_a was deleted, file_b exists.
|
||||
assert!(!at.file_exists(file_a));
|
||||
assert!(at.file_exists(file_b));
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
#[test]
|
||||
fn test_shred_force() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
|
||||
let file = "test_shred_force";
|
||||
|
||||
// Create file_a.
|
||||
at.touch(file);
|
||||
assert!(at.file_exists(file));
|
||||
|
||||
// Make file_a readonly.
|
||||
at.set_readonly(file);
|
||||
|
||||
// Try shred -u.
|
||||
let result = scene.ucmd().arg("-u").arg(file).run();
|
||||
println!("stderr = {:?}", result.stderr);
|
||||
println!("stdout = {:?}", result.stdout);
|
||||
|
||||
// file_a was not deleted because it is readonly.
|
||||
assert!(at.file_exists(file));
|
||||
|
||||
// Try shred -u -f.
|
||||
scene.ucmd().arg("-u").arg("-f").arg(file).run();
|
||||
|
||||
// file_a was deleted.
|
||||
assert!(!at.file_exists(file));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,50 @@
|
|||
use crate::common::util::*;
|
||||
|
||||
fn test_helper(file_name: &str, args: &str) {
|
||||
new_ucmd!()
|
||||
.arg(args)
|
||||
.arg(format!("{}.txt", file_name))
|
||||
.succeeds()
|
||||
.stdout_is_fixture(format!("{}.expected", file_name));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_months_whitespace() {
|
||||
test_helper("months-whitespace", "-M");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_empty_lines() {
|
||||
new_ucmd!()
|
||||
.arg("-V")
|
||||
.arg("version-empty-lines.txt")
|
||||
.succeeds()
|
||||
.stdout_is("\n\n\n\n\n\n\n1.2.3-alpha\n1.2.3-alpha2\n\t\t\t1.12.4\n11.2.3\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_human_numeric_whitespace() {
|
||||
test_helper("human-numeric-whitespace", "-h");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_decimals_general() {
|
||||
new_ucmd!()
|
||||
.arg("-g")
|
||||
.arg("multiple_decimals_general.txt")
|
||||
.succeeds()
|
||||
.stdout_is("\n\n\n\n\n\n\n\nCARAvan\n-2028789030\n-896689\n-8.90880\n-1\n-.05\n000\n00000001\n1\n1.040000000\n1.444\n1.58590\n8.013\n45\n46.89\n576,446.88800000\n576,446.890\n 4567.\n4567.1\n4567.34\n\t\t\t\t\t\t\t\t\t\t4567..457\n\t\t\t\t37800\n\t\t\t\t\t\t45670.89079.098\n\t\t\t\t\t\t45670.89079.1\n4798908.340000000000\n4798908.45\n4798908.8909800\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_decimals_numeric() {
|
||||
new_ucmd!()
|
||||
.arg("-n")
|
||||
.arg("multiple_decimals_numeric.txt")
|
||||
.succeeds()
|
||||
.stdout_is("-2028789030\n-896689\n-8.90880\n-1\n-.05\n\n\n\n\n\n\n\n\n000\nCARAvan\n00000001\n1\n1.040000000\n1.444\n1.58590\n8.013\n45\n46.89\n 4567.\n4567.1\n4567.34\n\t\t\t\t\t\t\t\t\t\t4567..457\n\t\t\t\t37800\n\t\t\t\t\t\t45670.89079.098\n\t\t\t\t\t\t45670.89079.1\n576,446.88800000\n576,446.890\n4798908.340000000000\n4798908.45\n4798908.8909800\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_zero_terminated_failure() {
|
||||
new_ucmd!()
|
||||
|
@ -44,6 +89,21 @@ fn test_random_shuffle_contains_all_lines() {
|
|||
assert_eq!(result_sorted, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_shuffle_two_runs_not_the_same() {
|
||||
// check to verify that two random shuffles are not equal; this has the
|
||||
// potential to fail in the very unlikely event that the random order is the same
|
||||
// as the starting order, or if both random sorts end up having the same order.
|
||||
const FILE: &'static str = "default_unsorted_ints.expected";
|
||||
let (at, _ucmd) = at_and_ucmd!();
|
||||
let result = new_ucmd!().arg("-R").arg(FILE).run().stdout;
|
||||
let expected = at.read(FILE);
|
||||
let unexpected = new_ucmd!().arg("-R").arg(FILE).run().stdout;
|
||||
|
||||
assert_ne!(result, expected);
|
||||
assert_ne!(result, unexpected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random_shuffle_contains_two_runs_not_the_same() {
|
||||
// check to verify that two random shuffles are not equal; this has the
|
||||
|
@ -144,10 +204,10 @@ fn test_dictionary_order2() {
|
|||
fn test_non_printing_chars() {
|
||||
for non_printing_chars_param in vec!["-i"] {
|
||||
new_ucmd!()
|
||||
.pipe_in("a👦🏻aa b\naaaa b")
|
||||
.pipe_in("a👦🏻aa\naaaa")
|
||||
.arg(non_printing_chars_param)
|
||||
.succeeds()
|
||||
.stdout_only("aaaa b\na👦🏻aa b\n");
|
||||
.stdout_only("a👦🏻aa\naaaa\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,6 +242,16 @@ fn test_mixed_floats_ints_chars_numeric_unique() {
|
|||
test_helper("mixed_floats_ints_chars_numeric_unique", "-nu");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_words_unique() {
|
||||
test_helper("words_unique", "-u");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_numeric_unique() {
|
||||
test_helper("numeric_unique", "-nu");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mixed_floats_ints_chars_numeric_reverse() {
|
||||
test_helper("mixed_floats_ints_chars_numeric_unique_reverse", "-nur");
|
||||
|
@ -266,6 +336,166 @@ fn test_numeric_unique_ints2() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_open_ended() {
|
||||
let input = "aa bb cc\ndd aa ff\ngg aa cc\n";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "2.2"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("gg aa cc\ndd aa ff\naa bb cc\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_closed_range() {
|
||||
let input = "aa bb cc\ndd aa ff\ngg aa cc\n";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "2.2,2.2"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("dd aa ff\ngg aa cc\naa bb cc\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_multiple_ranges() {
|
||||
let input = "aa bb cc\ndd aa ff\ngg aa cc\n";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "2,2", "-k", "3,3"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("gg aa cc\ndd aa ff\naa bb cc\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_no_field_match() {
|
||||
let input = "aa aa aa aa\naa bb cc\ndd aa ff\n";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "4,4"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("aa bb cc\ndd aa ff\naa aa aa aa\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_no_char_match() {
|
||||
let input = "aaa\nba\nc\n";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "1.2"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("c\nba\naaa\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_custom_separator() {
|
||||
let input = "aaxbbxcc\nddxaaxff\nggxaaxcc\n";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "2.2,2.2", "-t", "x"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("ddxaaxff\nggxaaxcc\naaxbbxcc\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_invalid_field() {
|
||||
new_ucmd!()
|
||||
.args(&["-k", "1."])
|
||||
.fails()
|
||||
.stderr_only("sort: error: failed to parse character index for key `1.`: cannot parse integer from empty string");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_invalid_field_option() {
|
||||
new_ucmd!()
|
||||
.args(&["-k", "1.1x"])
|
||||
.fails()
|
||||
.stderr_only("sort: error: invalid option for key: `x`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_invalid_field_zero() {
|
||||
new_ucmd!()
|
||||
.args(&["-k", "0.1"])
|
||||
.fails()
|
||||
.stderr_only("sort: error: field index was 0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_with_options() {
|
||||
let input = "aa 3 cc\ndd 1 ff\ngg 2 cc\n";
|
||||
for param in &[
|
||||
&["-k", "2,2n"][..],
|
||||
&["-k", "2n,2"][..],
|
||||
&["-k", "2,2", "-n"][..],
|
||||
] {
|
||||
new_ucmd!()
|
||||
.args(param)
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("dd 1 ff\ngg 2 cc\naa 3 cc\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_with_options_blanks_start() {
|
||||
let input = "aa 3 cc\ndd 1 ff\ngg 2 cc\n";
|
||||
for param in &[&["-k", "2b,2"][..], &["-k", "2,2", "-b"][..]] {
|
||||
new_ucmd!()
|
||||
.args(param)
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only("dd 1 ff\ngg 2 cc\naa 3 cc\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_with_options_blanks_end() {
|
||||
let input = "a b
|
||||
a b
|
||||
a b
|
||||
";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "1,2.1b", "-s"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only(
|
||||
"a b
|
||||
a b
|
||||
a b
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_stable() {
|
||||
let input = "a b
|
||||
a b
|
||||
a b
|
||||
";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "1,2.1", "-s"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only(
|
||||
"a b
|
||||
a b
|
||||
a b
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_empty_match() {
|
||||
let input = "a a a a
|
||||
aaaa
|
||||
";
|
||||
new_ucmd!()
|
||||
.args(&["-k", "1,1", "-t", "a"])
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_only(input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_terminated() {
|
||||
test_helper("zero-terminated", "-z");
|
||||
|
@ -355,11 +585,3 @@ fn test_check_silent() {
|
|||
.fails()
|
||||
.stdout_is("");
|
||||
}
|
||||
|
||||
fn test_helper(file_name: &str, args: &str) {
|
||||
new_ucmd!()
|
||||
.arg(args)
|
||||
.arg(format!("{}{}", file_name, ".txt"))
|
||||
.succeeds()
|
||||
.stdout_is_fixture(format!("{}{}", file_name, ".expected"));
|
||||
}
|
||||
|
|
|
@ -1,13 +1,65 @@
|
|||
#[cfg(not(target_os = "windows"))]
|
||||
use crate::common::util::*;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[test]
|
||||
fn test_stdbuf_unbuffered_stdout() {
|
||||
if cfg!(target_os = "linux") {
|
||||
// This is a basic smoke test
|
||||
new_ucmd!()
|
||||
.args(&["-o0", "head"])
|
||||
.pipe_in("The quick brown fox jumps over the lazy dog.")
|
||||
.run()
|
||||
.stdout_is("The quick brown fox jumps over the lazy dog.");
|
||||
}
|
||||
// This is a basic smoke test
|
||||
new_ucmd!()
|
||||
.args(&["-o0", "head"])
|
||||
.pipe_in("The quick brown fox jumps over the lazy dog.")
|
||||
.run()
|
||||
.stdout_is("The quick brown fox jumps over the lazy dog.");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[test]
|
||||
fn test_stdbuf_line_buffered_stdout() {
|
||||
new_ucmd!()
|
||||
.args(&["-oL", "head"])
|
||||
.pipe_in("The quick brown fox jumps over the lazy dog.")
|
||||
.run()
|
||||
.stdout_is("The quick brown fox jumps over the lazy dog.");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[test]
|
||||
fn test_stdbuf_no_buffer_option_fails() {
|
||||
new_ucmd!().args(&["head"]).fails().stderr_is(
|
||||
"error: The following required arguments were not provided:\n \
|
||||
--error <MODE>\n \
|
||||
--input <MODE>\n \
|
||||
--output <MODE>\n\n\
|
||||
USAGE:\n \
|
||||
stdbuf OPTION... COMMAND\n\n\
|
||||
For more information try --help",
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[test]
|
||||
fn test_stdbuf_trailing_var_arg() {
|
||||
new_ucmd!()
|
||||
.args(&["-i", "1024", "tail", "-1"])
|
||||
.pipe_in("The quick brown fox\njumps over the lazy dog.")
|
||||
.run()
|
||||
.stdout_is("jumps over the lazy dog.");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[test]
|
||||
fn test_stdbuf_line_buffering_stdin_fails() {
|
||||
new_ucmd!()
|
||||
.args(&["-i", "L", "head"])
|
||||
.fails()
|
||||
.stderr_is("stdbuf: error: line buffering stdin is meaningless\nTry 'stdbuf --help' for more information.");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[test]
|
||||
fn test_stdbuf_invalid_mode_fails() {
|
||||
new_ucmd!()
|
||||
.args(&["-i", "1024R", "head"])
|
||||
.fails()
|
||||
.stderr_is("stdbuf: error: invalid mode 1024R\nTry 'stdbuf --help' for more information.");
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ fn set_file_times(at: &AtPath, path: &str, atime: FileTime, mtime: FileTime) {
|
|||
fn str_to_filetime(format: &str, s: &str) -> FileTime {
|
||||
let mut tm = time::strptime(s, format).unwrap();
|
||||
tm.tm_utcoff = time::now().tm_utcoff;
|
||||
tm.tm_isdst = -1; // Unknown flag DST
|
||||
let ts = tm.to_timespec();
|
||||
FileTime::from_unix_time(ts.sec as i64, ts.nsec as u32)
|
||||
}
|
||||
|
@ -352,3 +353,21 @@ fn test_touch_set_date() {
|
|||
assert_eq!(atime, start_of_year);
|
||||
assert_eq!(mtime, start_of_year);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_touch_mtime_dst_succeeds() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file = "test_touch_set_mtime_dst_succeeds";
|
||||
|
||||
ucmd.args(&["-m", "-t", "202103140300", file])
|
||||
.succeeds()
|
||||
.no_stderr();
|
||||
|
||||
assert!(at.file_exists(file));
|
||||
|
||||
let target_time = str_to_filetime("%Y%m%d%H%M", "202103140300");
|
||||
let (_, mtime) = get_file_times(&at, file);
|
||||
eprintln!("target_time: {:?}", target_time);
|
||||
eprintln!("mtime: {:?}", mtime);
|
||||
assert!(target_time == mtime);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,33 @@
|
|||
use crate::common::util::*;
|
||||
|
||||
#[test]
|
||||
fn test_count_bytes_large_stdin() {
|
||||
for &n in &[
|
||||
0,
|
||||
1,
|
||||
42,
|
||||
16 * 1024 - 7,
|
||||
16 * 1024 - 1,
|
||||
16 * 1024,
|
||||
16 * 1024 + 1,
|
||||
16 * 1024 + 3,
|
||||
32 * 1024,
|
||||
64 * 1024,
|
||||
80 * 1024,
|
||||
96 * 1024,
|
||||
112 * 1024,
|
||||
128 * 1024,
|
||||
] {
|
||||
let data = vec_of_size(n);
|
||||
let expected = format!("{}\n", n);
|
||||
new_ucmd!()
|
||||
.args(&["-c"])
|
||||
.pipe_in(data)
|
||||
.succeeds()
|
||||
.stdout_is_bytes(&expected.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdin_default() {
|
||||
new_ucmd!()
|
||||
|
|
|
@ -1,40 +1,3 @@
|
|||
/// Assertion helper macro for [`CmdResult`] types
|
||||
///
|
||||
/// [`CmdResult`]: crate::tests::common::util::CmdResult
|
||||
#[macro_export]
|
||||
macro_rules! assert_empty_stderr(
|
||||
($cond:expr) => (
|
||||
if $cond.stderr.len() > 0 {
|
||||
panic!("stderr: {}", $cond.stderr_str())
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Assertion helper macro for [`CmdResult`] types
|
||||
///
|
||||
/// [`CmdResult`]: crate::tests::common::util::CmdResult
|
||||
#[macro_export]
|
||||
macro_rules! assert_empty_stdout(
|
||||
($cond:expr) => (
|
||||
if $cond.stdout.len() > 0 {
|
||||
panic!("stdout: {}", $cond.stdout_str())
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Assertion helper macro for [`CmdResult`] types
|
||||
///
|
||||
/// [`CmdResult`]: crate::tests::common::util::CmdResult
|
||||
#[macro_export]
|
||||
macro_rules! assert_no_error(
|
||||
($cond:expr) => (
|
||||
assert!($cond.success);
|
||||
if $cond.stderr.len() > 0 {
|
||||
panic!("stderr: {}", $cond.stderr_str())
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Platform-independent helper for constructing a PathBuf from individual elements
|
||||
#[macro_export]
|
||||
macro_rules! path_concat {
|
||||
|
|
|
@ -33,6 +33,8 @@ static ALREADY_RUN: &str = " you have already run this UCommand, if you want to
|
|||
testing();";
|
||||
static MULTIPLE_STDIN_MEANINGLESS: &str = "Ucommand is designed around a typical use case of: provide args and input stream -> spawn process -> block until completion -> return output streams. For verifying that a particular section of the input stream is what causes a particular behavior, use the Command type directly.";
|
||||
|
||||
static NO_STDIN_MEANINGLESS: &str = "Setting this flag has no effect if there is no stdin";
|
||||
|
||||
/// Test if the program is running under CI
|
||||
pub fn is_ci() -> bool {
|
||||
std::env::var("CI")
|
||||
|
@ -64,7 +66,7 @@ fn read_scenario_fixture<S: AsRef<OsStr>>(tmpd: &Option<Rc<TempDir>>, file_rel_p
|
|||
|
||||
/// A command result is the outputs of a command (streams and status code)
|
||||
/// within a struct which has convenience assertion functions about those outputs
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CmdResult {
|
||||
//tmpd is used for convenience functions for asserts against fixtures
|
||||
tmpd: Option<Rc<TempDir>>,
|
||||
|
@ -130,6 +132,11 @@ impl CmdResult {
|
|||
self.code.expect("Program must be run first")
|
||||
}
|
||||
|
||||
pub fn code_is(&self, expected_code: i32) -> &CmdResult {
|
||||
assert_eq!(self.code(), expected_code);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the program's TempDir
|
||||
/// Panics if not present
|
||||
pub fn tmpd(&self) -> Rc<TempDir> {
|
||||
|
@ -146,13 +153,25 @@ impl CmdResult {
|
|||
|
||||
/// asserts that the command resulted in a success (zero) status code
|
||||
pub fn success(&self) -> &CmdResult {
|
||||
assert!(self.success);
|
||||
if !self.success {
|
||||
panic!(
|
||||
"Command was expected to succeed.\nstdout = {}\n stderr = {}",
|
||||
self.stdout_str(),
|
||||
self.stderr_str()
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// asserts that the command resulted in a failure (non-zero) status code
|
||||
pub fn failure(&self) -> &CmdResult {
|
||||
assert!(!self.success);
|
||||
if self.success {
|
||||
panic!(
|
||||
"Command was expected to fail.\nstdout = {}\n stderr = {}",
|
||||
self.stdout_str(),
|
||||
self.stderr_str()
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -168,7 +187,12 @@ impl CmdResult {
|
|||
/// 1. you can not know exactly what stdout will be or
|
||||
/// 2. you know that stdout will also be empty
|
||||
pub fn no_stderr(&self) -> &CmdResult {
|
||||
assert!(self.stderr.is_empty());
|
||||
if !self.stderr.is_empty() {
|
||||
panic!(
|
||||
"Expected stderr to be empty, but it's:\n{}",
|
||||
self.stderr_str()
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -179,7 +203,12 @@ impl CmdResult {
|
|||
/// 1. you can not know exactly what stderr will be or
|
||||
/// 2. you know that stderr will also be empty
|
||||
pub fn no_stdout(&self) -> &CmdResult {
|
||||
assert!(self.stdout.is_empty());
|
||||
if !self.stdout.is_empty() {
|
||||
panic!(
|
||||
"Expected stdout to be empty, but it's:\n{}",
|
||||
self.stderr_str()
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -222,6 +251,12 @@ impl CmdResult {
|
|||
self
|
||||
}
|
||||
|
||||
/// Like stdout_is_fixture, but for stderr
|
||||
pub fn stderr_is_fixture<T: AsRef<OsStr>>(&self, file_rel_path: T) -> &CmdResult {
|
||||
let contents = read_scenario_fixture(&self.tmpd, file_rel_path);
|
||||
self.stderr_is_bytes(contents)
|
||||
}
|
||||
|
||||
/// asserts that
|
||||
/// 1. the command resulted in stdout stream output that equals the
|
||||
/// passed in value
|
||||
|
@ -271,10 +306,34 @@ impl CmdResult {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn stderr_contains<T: AsRef<str>>(&self, cmp: &T) -> &CmdResult {
|
||||
pub fn stderr_contains<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
|
||||
assert!(self.stderr_str().contains(cmp.as_ref()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stdout_does_not_contain<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
|
||||
assert!(!self.stdout_str().contains(cmp.as_ref()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stderr_does_not_contain<T: AsRef<str>>(&self, cmp: T) -> &CmdResult {
|
||||
assert!(!self.stderr_str().contains(cmp.as_ref()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stdout_matches(&self, regex: ®ex::Regex) -> &CmdResult {
|
||||
if !regex.is_match(self.stdout_str()) {
|
||||
panic!("Stdout does not match regex:\n{}", self.stdout_str())
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stdout_does_not_match(&self, regex: ®ex::Regex) -> &CmdResult {
|
||||
if regex.is_match(self.stdout_str()) {
|
||||
panic!("Stdout matches regex:\n{}", self.stdout_str())
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_info<T: AsRef<str>, U: AsRef<str>>(msg: T, par: U) {
|
||||
|
@ -351,6 +410,13 @@ impl AtPath {
|
|||
String::from(self.minus(name).to_str().unwrap())
|
||||
}
|
||||
|
||||
pub fn set_readonly(&self, name: &str) {
|
||||
let metadata = fs::metadata(self.plus(name)).unwrap();
|
||||
let mut permissions = metadata.permissions();
|
||||
permissions.set_readonly(true);
|
||||
fs::set_permissions(self.plus(name), permissions).unwrap();
|
||||
}
|
||||
|
||||
pub fn open(&self, name: &str) -> File {
|
||||
log_info("open", self.plus_as_string(name));
|
||||
File::open(self.plus(name)).unwrap()
|
||||
|
@ -624,6 +690,7 @@ pub struct UCommand {
|
|||
tmpd: Option<Rc<TempDir>>,
|
||||
has_run: bool,
|
||||
stdin: Option<Vec<u8>>,
|
||||
ignore_stdin_write_error: bool,
|
||||
}
|
||||
|
||||
impl UCommand {
|
||||
|
@ -653,6 +720,7 @@ impl UCommand {
|
|||
},
|
||||
comm_string: String::from(arg.as_ref().to_str().unwrap()),
|
||||
stdin: None,
|
||||
ignore_stdin_write_error: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,6 +773,17 @@ impl UCommand {
|
|||
self.pipe_in(contents)
|
||||
}
|
||||
|
||||
/// Ignores error caused by feeding stdin to the command.
|
||||
/// This is typically useful to test non-standard workflows
|
||||
/// like feeding something to a command that does not read it
|
||||
pub fn ignore_stdin_write_error(&mut self) -> &mut UCommand {
|
||||
if self.stdin.is_none() {
|
||||
panic!("{}", NO_STDIN_MEANINGLESS);
|
||||
}
|
||||
self.ignore_stdin_write_error = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut UCommand
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
|
@ -725,7 +804,7 @@ impl UCommand {
|
|||
}
|
||||
self.has_run = true;
|
||||
log_info("run", &self.comm_string);
|
||||
let mut result = self
|
||||
let mut child = self
|
||||
.raw
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
|
@ -734,15 +813,19 @@ impl UCommand {
|
|||
.unwrap();
|
||||
|
||||
if let Some(ref input) = self.stdin {
|
||||
result
|
||||
let write_result = child
|
||||
.stdin
|
||||
.take()
|
||||
.unwrap_or_else(|| panic!("Could not take child process stdin"))
|
||||
.write_all(input)
|
||||
.unwrap_or_else(|e| panic!("{}", e));
|
||||
.write_all(input);
|
||||
if !self.ignore_stdin_write_error {
|
||||
if let Err(e) = write_result {
|
||||
panic!("failed to write to stdin of child: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
child
|
||||
}
|
||||
|
||||
/// Spawns the command, feeds the stdin if any, waits for the result
|
||||
|
@ -797,3 +880,220 @@ pub fn read_size(child: &mut Child, size: usize) -> String {
|
|||
.unwrap();
|
||||
String::from_utf8(output).unwrap()
|
||||
}
|
||||
|
||||
pub fn vec_of_size(n: usize) -> Vec<u8> {
|
||||
let mut result = Vec::new();
|
||||
for _ in 0..n {
|
||||
result.push('a' as u8);
|
||||
}
|
||||
assert_eq!(result.len(), n);
|
||||
result
|
||||
}
|
||||
|
||||
/// Sanity checks for test utils
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_code_is() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: Some(32),
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.code_is(32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_code_is_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: Some(32),
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_failure() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_failure_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_success() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_success_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: false,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_std_errout() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
res.no_stderr();
|
||||
res.no_stdout();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_no_stderr_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "asdfsadfa".into(),
|
||||
};
|
||||
|
||||
res.no_stderr();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_no_stdout_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "asdfsadfa".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
|
||||
res.no_stdout();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_std_does_not_contain() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
res.stdout_does_not_contain("unlikely");
|
||||
res.stderr_does_not_contain("unlikely");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stdout_does_not_contain_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "".into(),
|
||||
};
|
||||
|
||||
res.stdout_does_not_contain("likely");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stderr_does_not_contain_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
|
||||
res.stderr_does_not_contain("likely");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdout_matches() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
let positive = regex::Regex::new(".*likely.*").unwrap();
|
||||
let negative = regex::Regex::new(".*unlikely.*").unwrap();
|
||||
res.stdout_matches(&positive);
|
||||
res.stdout_does_not_match(&negative);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stdout_matches_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
let negative = regex::Regex::new(".*unlikely.*").unwrap();
|
||||
|
||||
res.stdout_matches(&negative);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_stdout_not_matches_fail() {
|
||||
let res = CmdResult {
|
||||
tmpd: None,
|
||||
code: None,
|
||||
success: true,
|
||||
stdout: "This is a likely error message\n".into(),
|
||||
stderr: "This is a likely error message\n".into(),
|
||||
};
|
||||
let positive = regex::Regex::new(".*likely.*").unwrap();
|
||||
|
||||
res.stdout_does_not_match(&positive);
|
||||
}
|
||||
}
|
||||
|
|
0
tests/fixtures/cat/empty.txt
vendored
Normal file
0
tests/fixtures/cat/empty.txt
vendored
Normal file
5
tests/fixtures/cat/three_directories_and_file_and_stdin.stderr.expected
vendored
Normal file
5
tests/fixtures/cat/three_directories_and_file_and_stdin.stderr.expected
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
cat: test_directory3/test_directory4: Is a directory
|
||||
cat: filewhichdoesnotexist.txt: No such file or directory (os error 2)
|
||||
cat: test_directory3/test_directory5: Is a directory
|
||||
cat: test_directory3/../test_directory3/test_directory5: Is a directory
|
||||
cat: test_directory3: Is a directory
|
3
tests/fixtures/install/helloworld.rs
vendored
Normal file
3
tests/fixtures/install/helloworld.rs
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("Hello World!");
|
||||
}
|
BIN
tests/fixtures/install/helloworld_linux
vendored
Executable file
BIN
tests/fixtures/install/helloworld_linux
vendored
Executable file
Binary file not shown.
BIN
tests/fixtures/install/helloworld_macos
vendored
Executable file
BIN
tests/fixtures/install/helloworld_macos
vendored
Executable file
Binary file not shown.
11
tests/fixtures/sort/human-numeric-whitespace.expected
vendored
Normal file
11
tests/fixtures/sort/human-numeric-whitespace.expected
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
456K
|
||||
4568K
|
||||
456M
|
||||
6.2G
|
11
tests/fixtures/sort/human-numeric-whitespace.txt
vendored
Normal file
11
tests/fixtures/sort/human-numeric-whitespace.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
456K
|
||||
|
||||
456M
|
||||
|
||||
|
||||
4568K
|
||||
|
||||
6.2G
|
||||
|
20
tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected
vendored
Normal file
20
tests/fixtures/sort/mixed_floats_ints_chars_numeric_unique_reverse_stable.expected
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
4798908.8909800
|
||||
4798908.45
|
||||
4798908.340000000000
|
||||
576,446.890
|
||||
576,446.88800000
|
||||
37800
|
||||
4567.
|
||||
46.89
|
||||
45
|
||||
8.013
|
||||
1.58590
|
||||
1.444
|
||||
1.040000000
|
||||
1
|
||||
|
||||
-.05
|
||||
-1
|
||||
-8.90880
|
||||
-896689
|
||||
-2028789030
|
8
tests/fixtures/sort/months-whitespace.expected
vendored
Normal file
8
tests/fixtures/sort/months-whitespace.expected
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
|
||||
JAN
|
||||
FEb
|
||||
apr
|
||||
apr
|
||||
JUNNNN
|
||||
AUG
|
8
tests/fixtures/sort/months-whitespace.txt
vendored
Normal file
8
tests/fixtures/sort/months-whitespace.txt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
JAN
|
||||
JUNNNN
|
||||
AUG
|
||||
|
||||
apr
|
||||
apr
|
||||
|
||||
FEb
|
33
tests/fixtures/sort/multiple_decimals.expected
vendored
Normal file
33
tests/fixtures/sort/multiple_decimals.expected
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
-2028789030
|
||||
-896689
|
||||
-8.90880
|
||||
-1
|
||||
-.05
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
000
|
||||
CARAvan
|
||||
00000001
|
||||
1
|
||||
1.040000000
|
||||
1.444
|
||||
1.58590
|
||||
8.013
|
||||
45
|
||||
46.89
|
||||
4567..457
|
||||
4567.
|
||||
4567.1
|
||||
4567.34
|
||||
37800
|
||||
576,446.88800000
|
||||
576,446.890
|
||||
4798908.340000000000
|
||||
4798908.45
|
||||
4798908.8909800
|
35
tests/fixtures/sort/multiple_decimals_general.txt
vendored
Normal file
35
tests/fixtures/sort/multiple_decimals_general.txt
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
576,446.890
|
||||
576,446.88800000
|
||||
|
||||
4567.1
|
||||
4567..457
|
||||
45670.89079.1
|
||||
45670.89079.098
|
||||
4567.34
|
||||
4567.
|
||||
45
|
||||
46.89
|
||||
-1
|
||||
1
|
||||
00000001
|
||||
4798908.340000000000
|
||||
4798908.45
|
||||
4798908.8909800
|
||||
|
||||
|
||||
37800
|
||||
|
||||
-2028789030
|
||||
-896689
|
||||
CARAvan
|
||||
|
||||
-8.90880
|
||||
-.05
|
||||
1.444
|
||||
1.58590
|
||||
1.040000000
|
||||
|
||||
8.013
|
||||
|
||||
000
|
||||
|
35
tests/fixtures/sort/multiple_decimals_numeric.txt
vendored
Normal file
35
tests/fixtures/sort/multiple_decimals_numeric.txt
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
576,446.890
|
||||
576,446.88800000
|
||||
|
||||
4567.1
|
||||
4567..457
|
||||
45670.89079.1
|
||||
45670.89079.098
|
||||
4567.34
|
||||
4567.
|
||||
45
|
||||
46.89
|
||||
-1
|
||||
1
|
||||
00000001
|
||||
4798908.340000000000
|
||||
4798908.45
|
||||
4798908.8909800
|
||||
|
||||
|
||||
37800
|
||||
|
||||
-2028789030
|
||||
-896689
|
||||
CARAvan
|
||||
|
||||
-8.90880
|
||||
-.05
|
||||
1.444
|
||||
1.58590
|
||||
1.040000000
|
||||
|
||||
8.013
|
||||
|
||||
000
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue