mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 03:57:44 +00:00
Merge pull request #7260 from jfinkels/cat-error-append-same-file
cat: error when output is input and appending
This commit is contained in:
commit
569afcc139
2 changed files with 60 additions and 13 deletions
|
@ -4,30 +4,31 @@
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting ELOOP
|
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting ELOOP
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
|
||||||
use std::fs::{metadata, File};
|
use std::fs::{metadata, File};
|
||||||
use std::io::{self, IsTerminal, Read, Write};
|
use std::io::{self, IsTerminal, Read, Write};
|
||||||
use thiserror::Error;
|
|
||||||
use uucore::display::Quotable;
|
|
||||||
use uucore::error::UResult;
|
|
||||||
use uucore::fs::FileInformation;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
use std::os::fd::{AsFd, AsRawFd};
|
|
||||||
|
|
||||||
/// Linux splice support
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
mod splice;
|
|
||||||
|
|
||||||
/// Unix domain socket support
|
/// Unix domain socket support
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::net::Shutdown;
|
use std::net::Shutdown;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
use std::os::fd::{AsFd, AsRawFd};
|
||||||
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::FileTypeExt;
|
use std::os::unix::fs::FileTypeExt;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
||||||
|
use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use nix::fcntl::{fcntl, FcntlArg};
|
||||||
|
use thiserror::Error;
|
||||||
|
use uucore::display::Quotable;
|
||||||
|
use uucore::error::UResult;
|
||||||
|
use uucore::fs::FileInformation;
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
|
/// Linux splice support
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
mod splice;
|
||||||
|
|
||||||
const USAGE: &str = help_usage!("cat.md");
|
const USAGE: &str = help_usage!("cat.md");
|
||||||
const ABOUT: &str = help_about!("cat.md");
|
const ABOUT: &str = help_about!("cat.md");
|
||||||
|
|
||||||
|
@ -322,6 +323,24 @@ fn cat_handle<R: FdReadable>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this process is appending to stdout.
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn is_appending() -> bool {
|
||||||
|
let stdout = std::io::stdout();
|
||||||
|
let flags = match fcntl(stdout.as_raw_fd(), FcntlArg::F_GETFL) {
|
||||||
|
Ok(flags) => flags,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
// TODO Replace `1 << 10` with `nix::fcntl::Oflag::O_APPEND`.
|
||||||
|
let o_append = 1 << 10;
|
||||||
|
(flags & o_append) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
fn is_appending() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn cat_path(
|
fn cat_path(
|
||||||
path: &str,
|
path: &str,
|
||||||
options: &OutputOptions,
|
options: &OutputOptions,
|
||||||
|
@ -331,10 +350,16 @@ fn cat_path(
|
||||||
match get_input_type(path)? {
|
match get_input_type(path)? {
|
||||||
InputType::StdIn => {
|
InputType::StdIn => {
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
|
let in_info = FileInformation::from_file(&stdin)?;
|
||||||
let mut handle = InputHandle {
|
let mut handle = InputHandle {
|
||||||
reader: stdin,
|
reader: stdin,
|
||||||
is_interactive: std::io::stdin().is_terminal(),
|
is_interactive: std::io::stdin().is_terminal(),
|
||||||
};
|
};
|
||||||
|
if let Some(out_info) = out_info {
|
||||||
|
if in_info == *out_info && is_appending() {
|
||||||
|
return Err(CatError::OutputIsInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
cat_handle(&mut handle, options, state)
|
cat_handle(&mut handle, options, state)
|
||||||
}
|
}
|
||||||
InputType::Directory => Err(CatError::IsDirectory),
|
InputType::Directory => Err(CatError::IsDirectory),
|
||||||
|
|
|
@ -9,6 +9,8 @@ use crate::common::util::vec_of_size;
|
||||||
use crate::common::util::TestScenario;
|
use crate::common::util::TestScenario;
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
use rlimit::Resource;
|
use rlimit::Resource;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use std::fs::File;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
@ -646,3 +648,23 @@ fn test_u_ignored() {
|
||||||
.stdout_only("hello");
|
.stdout_only("hello");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn test_appending_same_input_output() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
||||||
|
at.write("foo", "content");
|
||||||
|
let foo_file = at.plus_as_string("foo");
|
||||||
|
|
||||||
|
let file_read = File::open(&foo_file).unwrap();
|
||||||
|
let file_write = OpenOptions::new().append(true).open(&foo_file).unwrap();
|
||||||
|
|
||||||
|
ucmd.set_stdin(file_read);
|
||||||
|
ucmd.set_stdout(file_write);
|
||||||
|
|
||||||
|
ucmd.run()
|
||||||
|
.failure()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("input file is output file");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue