mirror of
https://github.com/YaLTeR/wl-clipboard-rs.git
synced 2025-10-06 00:32:41 +02:00
Remove utils::copy_data()
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- **Breaking** Removed `utils::copy_data`. It forked into a `/usr/bin/env cat`
|
||||
for copying. All internal uses of the function have been changed to simply
|
||||
use `std::io::copy` instead.
|
||||
|
||||
## v0.8.1 (7th Mar 2024)
|
||||
|
||||
- Updated dependencies, notably `nix`, which fixes building on LoongArch.
|
||||
|
15
src/copy.rs
15
src/copy.rs
@@ -5,7 +5,6 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{remove_dir, remove_file, File, OpenOptions};
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
use std::os::unix::io::IntoRawFd;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::mpsc::sync_channel;
|
||||
use std::{iter, thread};
|
||||
@@ -29,7 +28,7 @@ use wayland_protocols_wlr::data_control::v1::client::zwlr_data_control_source_v1
|
||||
|
||||
use crate::common::{self, initialize};
|
||||
use crate::seat_data::SeatData;
|
||||
use crate::utils::{self, copy_data, is_text};
|
||||
use crate::utils::is_text;
|
||||
|
||||
/// The clipboard to operate on.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, PartialOrd, Ord, Default)]
|
||||
@@ -157,7 +156,7 @@ pub enum SourceCreationError {
|
||||
TempFileCreate(#[source] io::Error),
|
||||
|
||||
#[error("Couldn't copy data to the temporary file")]
|
||||
DataCopy(#[source] utils::CopyDataError),
|
||||
DataCopy(#[source] io::Error),
|
||||
|
||||
#[error("Couldn't write to the temporary file")]
|
||||
TempFileWrite(#[source] io::Error),
|
||||
@@ -238,7 +237,7 @@ pub enum DataSourceError {
|
||||
FileOpen(#[source] io::Error),
|
||||
|
||||
#[error("Couldn't copy the data to the target file descriptor")]
|
||||
Copy(#[source] utils::CopyDataError),
|
||||
Copy(#[source] io::Error),
|
||||
}
|
||||
|
||||
struct State {
|
||||
@@ -350,9 +349,9 @@ impl Dispatch<ZwlrDataControlSourceV1, ()> for State {
|
||||
let data_path = &state.data_paths[&mime_type];
|
||||
|
||||
let file = File::open(data_path).map_err(DataSourceError::FileOpen);
|
||||
let result = file.and_then(|data_file| {
|
||||
let data_fd = data_file.into_raw_fd();
|
||||
copy_data(Some(data_fd), fd.into_raw_fd(), true).map_err(DataSourceError::Copy)
|
||||
let result = file.and_then(|mut data_file| {
|
||||
let mut target_file = File::from(fd);
|
||||
io::copy(&mut data_file, &mut target_file).map_err(DataSourceError::Copy)
|
||||
});
|
||||
|
||||
if let Err(err) = result {
|
||||
@@ -618,7 +617,7 @@ fn make_source(
|
||||
.map_err(SourceCreationError::TempFileWrite)?;
|
||||
} else {
|
||||
// Copy the standard input into the target file.
|
||||
copy_data(None, temp_file.into_raw_fd(), true).map_err(SourceCreationError::DataCopy)?;
|
||||
io::copy(&mut io::stdin(), &mut temp_file).map_err(SourceCreationError::DataCopy)?;
|
||||
}
|
||||
|
||||
let mime_type = match mime_type {
|
||||
|
142
src/utils.rs
142
src/utils.rs
@@ -1,16 +1,10 @@
|
||||
//! Helper functions.
|
||||
|
||||
use std::ffi::{CString, OsString};
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ffi::OsString;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::PathBuf;
|
||||
use std::process::abort;
|
||||
use std::{env, io};
|
||||
|
||||
use libc::{STDIN_FILENO, STDOUT_FILENO};
|
||||
use nix::fcntl::{fcntl, FcntlArg, OFlag};
|
||||
use nix::sys::wait::{waitpid, WaitStatus};
|
||||
use nix::unistd::{close, dup2, execv, fork, ForkResult};
|
||||
use wayland_client::protocol::wl_registry::{self, WlRegistry};
|
||||
use wayland_client::protocol::wl_seat::WlSeat;
|
||||
use wayland_client::{
|
||||
@@ -40,140 +34,6 @@ pub fn is_text(mime_type: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur in `copy_data()`.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum CopyDataError {
|
||||
#[error("Couldn't set the source file descriptor flags")]
|
||||
SetSourceFdFlags(#[source] nix::Error),
|
||||
|
||||
#[error("Couldn't set the target file descriptor flags")]
|
||||
SetTargetFdFlags(#[source] nix::Error),
|
||||
|
||||
#[error("Couldn't fork")]
|
||||
Fork(#[source] nix::Error),
|
||||
|
||||
#[error("Couldn't close the source file descriptor")]
|
||||
CloseSourceFd(#[source] nix::Error),
|
||||
|
||||
#[error("Couldn't close the target file descriptor")]
|
||||
CloseTargetFd(#[source] nix::Error),
|
||||
|
||||
#[error("Couldn't wait for the child process")]
|
||||
Wait(#[source] nix::Error),
|
||||
|
||||
#[error(
|
||||
"Received an unexpected status when waiting for the child process: {:?}",
|
||||
_0
|
||||
)]
|
||||
WaitUnexpected(WaitStatus),
|
||||
|
||||
#[error("The child process exited with a non-zero error code: {}", _0)]
|
||||
ChildError(i32),
|
||||
}
|
||||
|
||||
/// Copies data from one file to another.
|
||||
///
|
||||
/// This function assumes ownership of the passed file descriptors. That is, it closes them by
|
||||
/// itself. Use `into_raw_fd()`.
|
||||
///
|
||||
/// If `from_fd` is `None`, the standard input is used as the data source.
|
||||
///
|
||||
/// If `wait` is `true`, this function returns after all data has been copied, otherwise it may
|
||||
/// return before all data has been copied.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # extern crate wl_clipboard_rs;
|
||||
/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// use std::{fs::File, os::unix::io::IntoRawFd};
|
||||
/// use wl_clipboard_rs::utils::copy_data;
|
||||
///
|
||||
/// let file = File::create("stdin-contents")?;
|
||||
///
|
||||
/// // Copy the standard input into the file.
|
||||
/// copy_data(None, file.into_raw_fd(), true)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(unsafe_code)]
|
||||
pub fn copy_data(from_fd: Option<RawFd>, to_fd: RawFd, wait: bool) -> Result<(), CopyDataError> {
|
||||
// We use the cat utility for data copying. It's easier (no need to implement any complex
|
||||
// buffering logic), surprisingly safer (a Rust implementation would likely require usage of
|
||||
// `from_raw_fd()` which is unsafe) and ideally faster (cat's been around for a while and is
|
||||
// probably pretty optimized).
|
||||
|
||||
// Clear O_NONBLOCK because cat doesn't know how to deal with it.
|
||||
if let Some(from_fd) = from_fd {
|
||||
fcntl(from_fd, FcntlArg::F_SETFL(OFlag::empty()))
|
||||
.map_err(CopyDataError::SetSourceFdFlags)?;
|
||||
}
|
||||
fcntl(to_fd, FcntlArg::F_SETFL(OFlag::empty())).map_err(CopyDataError::SetTargetFdFlags)?;
|
||||
|
||||
// Don't allocate memory in the child process, it's not async-signal-safe.
|
||||
let bin_env = CString::new("/usr/bin/env").unwrap();
|
||||
let env = CString::new("env").unwrap();
|
||||
let cat = CString::new("cat").unwrap();
|
||||
|
||||
// Fork and exec cat.
|
||||
// SAFETY: Within the child, we are only using the following system calls: dup2, close, execv
|
||||
// As required by the safety of `fork`, these are all [async-signal-safe](https://man7.org/linux/man-pages/man7/signal-safety.7.html).
|
||||
let fork_result = unsafe { fork() }.map_err(CopyDataError::Fork)?;
|
||||
match fork_result {
|
||||
ForkResult::Child => {
|
||||
if let Some(fd) = from_fd {
|
||||
// Redirect the "from" fd to stdin.
|
||||
if dup2(fd, STDIN_FILENO).is_err() {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect stdout to the "to" fd.
|
||||
if dup2(to_fd, STDOUT_FILENO).is_err() {
|
||||
abort();
|
||||
}
|
||||
|
||||
// Close the original fds.
|
||||
if let Some(fd) = from_fd {
|
||||
if close(fd).is_err() {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if close(to_fd).is_err() {
|
||||
abort();
|
||||
}
|
||||
|
||||
// Exec cat.
|
||||
if execv(&bin_env, &[&env, &cat]).is_err() {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
ForkResult::Parent { child } => {
|
||||
// Close the fds in the parent process.
|
||||
if let Some(fd) = from_fd {
|
||||
close(fd).map_err(CopyDataError::CloseSourceFd)?;
|
||||
}
|
||||
|
||||
close(to_fd).map_err(CopyDataError::CloseTargetFd)?;
|
||||
|
||||
if wait {
|
||||
// Wait for the child process to exit.
|
||||
match waitpid(child, None).map_err(CopyDataError::Wait)? {
|
||||
WaitStatus::Exited(_, status) => {
|
||||
if status != 0 {
|
||||
return Err(CopyDataError::ChildError(status));
|
||||
}
|
||||
}
|
||||
x => return Err(CopyDataError::WaitUnexpected(x)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct PrimarySelectionState {
|
||||
// Any seat that we get from the compositor.
|
||||
seat: Option<WlSeat>,
|
||||
|
Reference in New Issue
Block a user