1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

cat: error when output is input and appending

Change `cat` so that it terminates with an error message when the input
file is the same as the output file and the output file is being
appended to. For example,

    cat <f >>f
    cat: -: input file is output file

Fixes #7165
This commit is contained in:
Jeffrey Finkelstein 2025-02-02 18:00:04 -05:00
parent e0a7c318a3
commit c9312eba9a

View file

@ -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),