Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/uu/cat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ clap = { workspace = true }
fluent = { workspace = true }
memchr = { workspace = true }
thiserror = { workspace = true }
uucore = { workspace = true, features = ["fast-inc", "fs", "pipes", "signals"] }
uucore = { workspace = true, features = [
"fast-inc",
"fs",
"pipes",
"signals",
"stdio",
] }

[dev-dependencies]
divan = { workspace = true }
Expand Down
36 changes: 15 additions & 21 deletions src/uu/cat/src/cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ use memchr::memchr2;
use std::ffi::OsString;
use std::fs::{File, metadata};
use std::io::{self, BufWriter, ErrorKind, IsTerminal, Read, Write};
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(not(windows))]
use std::os::fd::AsFd;
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use uucore::display::Quotable;
use uucore::error::{UResult, strip_errno};
#[cfg(not(windows))]
use uucore::stdio;
use uucore::translate;
use uucore::{fast_inc::fast_inc_one, format_usage};

Expand Down Expand Up @@ -170,14 +172,14 @@ struct OutputState {
one_blank_kept: bool,
}

#[cfg(any(unix, target_os = "wasi"))]
#[cfg(not(windows))]
trait FdReadable: Read + AsFd {}
#[cfg(not(any(unix, target_os = "wasi")))]
#[cfg(windows)]
trait FdReadable: Read {}

#[cfg(any(unix, target_os = "wasi"))]
#[cfg(not(windows))]
impl<T> FdReadable for T where T: Read + AsFd {}
#[cfg(not(any(unix, target_os = "wasi")))]
#[cfg(windows)]
impl<T> FdReadable for T where T: Read {}

/// Represents an open file handle, stream, or other device
Expand Down Expand Up @@ -491,38 +493,30 @@ fn get_input_type(path: &OsString) -> CatResult<InputType> {
/// Writes handle to stdout with no configuration. This allows a
/// simple memory copy.
fn print_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
let stdout = io::stdout();
#[cfg(any(target_os = "linux", target_os = "android"))]
let mut stdout = stdout;
// Try to use the splice() system call for faster writing. If it works, we're done.
#[cfg(any(target_os = "linux", target_os = "android"))]
if uucore::pipes::splice_unbounded_auto(&handle.reader, &mut stdout)?.is_ok() {
if uucore::pipes::splice_unbounded_auto(&handle.reader, &mut *stdio::stdout_raw())?.is_ok() {
return Ok(());
}

// If we're not on Linux or Android, or the splice() call failed,
// fall back on slower writing.
print_unbuffered(handle, stdout)
print_unbuffered(handle)
}

#[cfg_attr(any(target_os = "linux", target_os = "android"), inline(never))] // splice fast-path does not require this allocation
fn print_unbuffered<R: FdReadable>(
handle: &mut InputHandle<R>,
stdout: io::Stdout,
) -> CatResult<()> {
#[cfg(any(unix, target_os = "wasi"))]
let mut stdout = uucore::io::RawWriter(stdout); // use raw syscall to remove buffering
#[cfg(not(any(unix, target_os = "wasi")))]
let mut stdout = stdout.lock();
fn print_unbuffered<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
#[cfg(not(windows))]
let stdout = &mut *stdio::stdout_raw();
#[cfg(windows)]
let stdout = &mut io::stdout().lock();
let mut buf = [0; 1024 * 64];
loop {
match handle.reader.read(&mut buf) {
Ok(0) => return Ok(()),
Ok(n) => {
stdout.write_all(&buf[..n]).map_err(CatError::Write)?;
// cannot use rustix::io on Windows
// really bad workaround for unbuffered write <https://github.com/uutils/coreutils/issues/12188>
#[cfg(not(any(unix, target_os = "wasi")))]
#[cfg(windows)]
stdout.flush().map_err(CatError::Write)?;
}
Err(e) if e.kind() != ErrorKind::Interrupted => return Err(e.into()),
Expand Down
1 change: 1 addition & 0 deletions src/uucore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ safe-traversal = ["libc", "nix/fs", "nix/dir", "nix/user", "rustix/fs"]
selinux = ["dep:selinux", "libc"]
smack = ["xattr"]
signals = ["nix/fs", "nix/signal", "nix/poll"]
stdio = ["libc"]
sum = [
"digest",
"hex",
Expand Down
2 changes: 2 additions & 0 deletions src/uucore/src/lib/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ pub mod selinux;
pub mod signals;
#[cfg(all(feature = "smack", target_os = "linux"))]
pub mod smack;
#[cfg(all(feature = "stdio", any(unix, target_os = "hermit", target_os = "wasi")))]
pub mod stdio;
#[cfg(feature = "feat_systemd_logind")]
pub mod systemd_logind;
#[cfg(all(
Expand Down
80 changes: 80 additions & 0 deletions src/uucore/src/lib/features/stdio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

//! Abstractions for raw access to stdin and stdout, without buffering.

use core::ops::{Deref, DerefMut};
use std::fs::File;
use std::mem::ManuallyDrop;
use std::os::fd::{FromRawFd as _, OwnedFd, RawFd};

pub struct StdinRaw(ManuallyDrop<File>);

pub struct StdoutRaw(ManuallyDrop<File>);

pub struct StderrRaw(ManuallyDrop<File>);

pub fn stdin_raw() -> StdinRaw {
// SAFETY: We ensure that the file descriptor is never closed by
// wrapping the `File` in `ManuallyDrop`.
let fd = unsafe { OwnedFd::from_raw_fd(libc::STDIN_FILENO as RawFd) };
StdinRaw(ManuallyDrop::new(File::from(fd)))
}

pub fn stdout_raw() -> StdoutRaw {
// SAFETY: We ensure that the file descriptor is never closed by
// wrapping the `File` in `ManuallyDrop`.
let fd = unsafe { OwnedFd::from_raw_fd(libc::STDOUT_FILENO as RawFd) };
StdoutRaw(ManuallyDrop::new(File::from(fd)))
}

pub fn stderr_raw() -> StderrRaw {
// SAFETY: We ensure that the file descriptor is never closed by
// wrapping the `File` in `ManuallyDrop`.
let fd = unsafe { OwnedFd::from_raw_fd(libc::STDERR_FILENO as RawFd) };
StderrRaw(ManuallyDrop::new(File::from(fd)))
}

impl Deref for StdinRaw {
type Target = File;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for StdinRaw {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl Deref for StdoutRaw {
type Target = File;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for StdoutRaw {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl Deref for StderrRaw {
type Target = File;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for StderrRaw {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
2 changes: 2 additions & 0 deletions src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub use crate::features::quoting_style;
pub use crate::features::ranges;
#[cfg(feature = "ringbuffer")]
pub use crate::features::ringbuffer;
#[cfg(all(feature = "stdio", any(unix, target_os = "hermit", target_os = "wasi")))]
pub use crate::features::stdio;
#[cfg(feature = "sum")]
pub use crate::features::sum;
#[cfg(feature = "feat_systemd_logind")]
Expand Down
Loading