@@ -198,6 +198,8 @@ fn main() -> Result<()> {
let mut paths = vec![];
let mut urls = vec![];
let mut stdin_tmp_file: Option<fs::File> = None;
+ let mut anonymous_fd_tmp_files = vec![];
+
for path in args.paths_with_position.iter() {
if path.starts_with("zed://")
|| path.starts_with("http://")
@@ -211,6 +213,11 @@ fn main() -> Result<()> {
paths.push(file.path().to_string_lossy().to_string());
let (file, _) = file.keep()?;
stdin_tmp_file = Some(file);
+ } else if let Some(file) = anonymous_fd(path) {
+ let tmp_file = NamedTempFile::new()?;
+ paths.push(tmp_file.path().to_string_lossy().to_string());
+ let (tmp_file, _) = tmp_file.keep()?;
+ anonymous_fd_tmp_files.push((file, tmp_file));
} else {
paths.push(parse_path_with_position(path)?)
}
@@ -252,31 +259,33 @@ fn main() -> Result<()> {
}
});
- let pipe_handle: JoinHandle<anyhow::Result<()>> = thread::spawn(move || {
- if let Some(mut tmp_file) = stdin_tmp_file {
- let mut stdin = std::io::stdin().lock();
- if io::IsTerminal::is_terminal(&stdin) {
- return Ok(());
- }
- let mut buffer = [0; 8 * 1024];
- loop {
- let bytes_read = io::Read::read(&mut stdin, &mut buffer)?;
- if bytes_read == 0 {
- break;
+ let stdin_pipe_handle: Option<JoinHandle<anyhow::Result<()>>> =
+ stdin_tmp_file.map(|tmp_file| {
+ thread::spawn(move || {
+ let stdin = std::io::stdin().lock();
+ if io::IsTerminal::is_terminal(&stdin) {
+ return Ok(());
}
- io::Write::write(&mut tmp_file, &buffer[..bytes_read])?;
- }
- io::Write::flush(&mut tmp_file)?;
- }
- Ok(())
- });
+ return pipe_to_tmp(stdin, tmp_file);
+ })
+ });
+
+ let anonymous_fd_pipe_handles: Vec<JoinHandle<anyhow::Result<()>>> = anonymous_fd_tmp_files
+ .into_iter()
+ .map(|(file, tmp_file)| thread::spawn(move || pipe_to_tmp(file, tmp_file)))
+ .collect();
if args.foreground {
app.run_foreground(url)?;
} else {
app.launch(url)?;
sender.join().unwrap()?;
- pipe_handle.join().unwrap()?;
+ if let Some(handle) = stdin_pipe_handle {
+ handle.join().unwrap()?;
+ }
+ for handle in anonymous_fd_pipe_handles {
+ handle.join().unwrap()?;
+ }
}
if let Some(exit_status) = exit_status.lock().take() {
@@ -285,6 +294,64 @@ fn main() -> Result<()> {
Ok(())
}
+fn pipe_to_tmp(mut src: impl io::Read, mut dest: fs::File) -> Result<()> {
+ let mut buffer = [0; 8 * 1024];
+ loop {
+ let bytes_read = match src.read(&mut buffer) {
+ Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
+ res => res?,
+ };
+ if bytes_read == 0 {
+ break;
+ }
+ io::Write::write_all(&mut dest, &buffer[..bytes_read])?;
+ }
+ io::Write::flush(&mut dest)?;
+ Ok(())
+}
+
+fn anonymous_fd(path: &str) -> Option<fs::File> {
+ #[cfg(target_os = "linux")]
+ {
+ use std::os::fd::{self, FromRawFd};
+
+ let fd_str = path.strip_prefix("/proc/self/fd/")?;
+
+ let link = fs::read_link(path).ok()?;
+ if !link.starts_with("memfd:") {
+ return None;
+ }
+
+ let fd: fd::RawFd = fd_str.parse().ok()?;
+ let file = unsafe { fs::File::from_raw_fd(fd) };
+ return Some(file);
+ }
+ #[cfg(target_os = "macos")]
+ {
+ use std::os::{
+ fd::{self, FromRawFd},
+ unix::fs::FileTypeExt,
+ };
+
+ let fd_str = path.strip_prefix("/dev/fd/")?;
+
+ let metadata = fs::metadata(path).ok()?;
+ let file_type = metadata.file_type();
+ if !file_type.is_fifo() && !file_type.is_socket() {
+ return None;
+ }
+ let fd: fd::RawFd = fd_str.parse().ok()?;
+ let file = unsafe { fs::File::from_raw_fd(fd) };
+ return Some(file);
+ }
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
+ {
+ _ = path;
+ // not implemented for bsd, windows. Could be, but isn't yet
+ return None;
+ }
+}
+
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
mod linux {
use std::{