diff --git a/crates/std/build.rs b/crates/std/build.rs new file mode 100644 index 0000000..f151fa8 --- /dev/null +++ b/crates/std/build.rs @@ -0,0 +1,8 @@ +use std::env; + +pub fn main() { + println!( + "cargo:rustc-env=STD_ENV_ARCH={}", + env::var("CARGO_CFG_TARGET_ARCH").unwrap() + ); +} diff --git a/crates/std/justfile b/crates/std/justfile new file mode 100644 index 0000000..fcbc083 --- /dev/null +++ b/crates/std/justfile @@ -0,0 +1,179 @@ +SRC_DIR := "src" +PATCH_DIR := "patches" +RUST_SRC := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src" + +patch-std: + @echo "Start patching the std..." + @find {{ PATCH_DIR }} -type f | while read -r patch_file; do \ + relative_path="${patch_file#{{ PATCH_DIR }}/}"; \ + case "$patch_file" in \ + *.rs) \ + echo "📄 [COPY] $relative_path"; \ + mkdir -p "{{ SRC_DIR }}/$(dirname "$relative_path")"; \ + cp "$patch_file" "{{ SRC_DIR }}/$relative_path"; \ + ;; \ + *.sed) \ + target_file="${relative_path%.sed}.rs"; \ + if [ -f "{{ SRC_DIR }}/$target_file" ]; then \ + echo " [SED] $target_file"; \ + sed -i -f "$patch_file" "{{ SRC_DIR }}/$target_file"; \ + else \ + echo "⚠️[WARN] target doesn't exist: $target_file"; \ + fi \ + ;; \ + *) \ + echo "❓ [SKIP] Unknown file : $relative_path"; \ + ;; \ + esac; \ + done + @echo "✅ Patching done." + +update-std: + @just setup-std + @just patch-std + +cp_std path: + @echo "Copying {{ path }}" + @mkdir {{ "src" / parent_directory(path) }} -p + @cp {{ RUST_SRC / path }} {{ "src" / path }} + @sed -i -f patches.sed {{ "src" / path }} + +setup-std: + @# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs + # @just cp_std "process.rs" + # @just cp_std "fs.rs" + # @just cp_std "collections/mod.rs" + @just cp_std "env.rs" + @just cp_std "time.rs" + @just cp_std "os/mod.rs" + @just cp_std "os/raw/mod.rs" + @just cp_std "os/raw/tests.rs" + @just cp_std "bstr.rs" + @just cp_std "panicking.rs" + @just cp_std "panic.rs" + @just cp_std "collections/hash/map.rs" + @just cp_std "collections/hash/set.rs" + @just cp_std "collections/hash/mod.rs" + @just cp_std "io/error.rs" + @just cp_std "io/error/repr_bitpacked.rs" + @just cp_std "io/error/repr_unpacked.rs" + @just cp_std "io/error/tests.rs" + @just cp_std "io/cursor.rs" + @just cp_std "io/cursor/tests.rs" + @just cp_std "io/prelude.rs" + @just cp_std "io/impls.rs" + @just cp_std "io/impls/tests.rs" + @just cp_std "io/tests.rs" + @just cp_std "io/util.rs" + @just cp_std "io/util/tests.rs" + @just cp_std "io/copy.rs" + @just cp_std "io/copy/tests.rs" + @just cp_std "io/pipe.rs" + @just cp_std "io/pipe/tests.rs" + @just cp_std "io/stdio.rs" + @just cp_std "io/buffered/mod.rs" + @just cp_std "io/buffered/bufreader.rs" + @just cp_std "io/buffered/bufreader/buffer.rs" + @just cp_std "io/buffered/bufwriter.rs" + @just cp_std "io/buffered/linewriter.rs" + @just cp_std "io/buffered/linewritershim.rs" + @just cp_std "sync/once.rs" + @just cp_std "sync/once_lock.rs" + @just cp_std "sync/lazy_lock.rs" + @just cp_std "sync/nonpoison.rs" + @just cp_std "sync/nonpoison/condvar.rs" + @just cp_std "sync/nonpoison/mutex.rs" + @just cp_std "sync/nonpoison/rwlock.rs" + @just cp_std "sync/poison.rs" + @just cp_std "sync/poison/condvar.rs" + @just cp_std "sync/poison/mutex.rs" + @just cp_std "sync/poison/rwlock.rs" + @just cp_std "sync/barrier.rs" + @just cp_std "sync/reentrant_lock.rs" + @just cp_std "sync/mpsc.rs" + @just cp_std "sync/mpmc/mod.rs" + @just cp_std "sync/mpmc/array.rs" + @just cp_std "sync/mpmc/context.rs" + @just cp_std "sync/mpmc/counter.rs" + @just cp_std "sync/mpmc/error.rs" + @just cp_std "sync/mpmc/list.rs" + @just cp_std "sync/mpmc/select.rs" + @just cp_std "sync/mpmc/tests.rs" + @just cp_std "sync/mpmc/utils.rs" + @just cp_std "sync/mpmc/waker.rs" + @just cp_std "sync/mpmc/zero.rs" + @just cp_std "hash/mod.rs" + @just cp_std "hash/random.rs" + @just cp_std "num/mod.rs" + @just cp_std "ffi/c_str.rs" + @just cp_std "ffi/mod.rs" + @just cp_std "ffi/os_str.rs" + @just cp_std "ffi/os_str/tests.rs" + @just cp_std "thread/local.rs" + @just cp_std "thread/thread.rs" + @just cp_std "thread/id.rs" + @just cp_std "thread/main_thread.rs" + @just cp_std "thread/current.rs" + @just cp_std "thread/join_handle.rs" + @just cp_std "thread/functions.rs" + @just cp_std "thread/lifecycle.rs" + @just cp_std "thread/builder.rs" + @just cp_std "thread/scoped.rs" + @just cp_std "thread/spawnhook.rs" + @just cp_std "sys/exit.rs" + @just cp_std "sys/env_consts.rs" + @just cp_std "sys/configure_builtins.rs" + @just cp_std "sys/cmath.rs" + @just cp_std "sys/process/mod.rs" + @just cp_std "sys/process/env.rs" + @just cp_std "sys/args/mod.rs" + @just cp_std "sys/args/unsupported.rs" + @just cp_std "sys/pal/mod.rs" + @just cp_std "sys/pal/unsupported/mod.rs" + @just cp_std "sys/pal/unsupported/common.rs" + @just cp_std "sys/pal/unsupported/os.rs" + @just cp_std "sys/sync/condvar/mod.rs" + @just cp_std "sys/sync/condvar/no_threads.rs" + @just cp_std "sys/sync/mutex/mod.rs" + @just cp_std "sys/sync/mutex/no_threads.rs" + @just cp_std "sys/sync/once/mod.rs" + @just cp_std "sys/sync/once/no_threads.rs" + @just cp_std "sys/sync/rwlock/mod.rs" + @just cp_std "sys/sync/rwlock/no_threads.rs" + @just cp_std "sys/sync/thread_parking/mod.rs" + @just cp_std "sys/sync/thread_parking/unsupported.rs" + @just cp_std "sys/thread/mod.rs" + @just cp_std "sys/thread/unsupported.rs" + @just cp_std "sys/thread_local/no_threads.rs" + @just cp_std "sys/thread_local/os.rs" + @just cp_std "sys/thread_local/mod.rs" + @just cp_std "sys/time/mod.rs" + @just cp_std "sys/time/unsupported.rs" + @just cp_std "sys/random/mod.rs" + @just cp_std "sys/random/unsupported.rs" + @just cp_std "sys/env/mod.rs" + @just cp_std "sys/env/common.rs" + @just cp_std "sys/env/unsupported.rs" + @just cp_std "sys/os_str/mod.rs" + @just cp_std "sys/os_str/bytes.rs" + @just cp_std "sys/os_str/bytes/tests.rs" + @just cp_std "sys/path/mod.rs" + @just cp_std "sys/path/unix.rs" + @just cp_std "sys/fs/mod.rs" + @just cp_std "sys/fs/common.rs" + @just cp_std "sys/fs/unsupported.rs" + @just cp_std "sys/io/error/generic.rs" + @just cp_std "sys/io/io_slice/unsupported.rs" + @just cp_std "sys/io/is_terminal/unsupported.rs" + @just cp_std "sys/io/kernel_copy/mod.rs" + @just cp_std "sys/io/mod.rs" + @just cp_std "sys/pipe/mod.rs" + @just cp_std "sys/pipe/unsupported.rs" + @just cp_std "sys/stdio/mod.rs" + @just cp_std "sys/stdio/unsupported.rs" + @just cp_std "sys/alloc/mod.rs" + @# Copied but edited for the moment + # @just cp_std "sys/process/unsupported.rs" + @just cp_std "sys/io/error/mod.rs" + # @just cp_std "alloc.rs" + # @just cp_std "path.rs" diff --git a/patches.sed b/crates/std/patches.sed similarity index 87% rename from patches.sed rename to crates/std/patches.sed index 110f8a0..6c95e2d 100644 --- a/patches.sed +++ b/crates/std/patches.sed @@ -7,6 +7,8 @@ s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError| s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g # s|collections::HashMap|hashbrown::HashMap|g /crate::backtrace_rs/c \ todo!() +/crate::sys::os::getpid/c \ todo!() +/\[doc = include_str!/c \ // todo retreive docs # /target_os = "xous",/a \ target_os = "survos", # Ajouter d'autres modifications facilement ici : diff --git a/crates/std/patches/sys/alloc/mod.sed b/crates/std/patches/sys/alloc/mod.sed new file mode 100644 index 0000000..22d2e31 --- /dev/null +++ b/crates/std/patches/sys/alloc/mod.sed @@ -0,0 +1,3 @@ +109a \ target_os = "survos" => { \ + mod survos; \ + } diff --git a/crates/std/patches/sys/args/mod.sed b/crates/std/patches/sys/args/mod.sed new file mode 100644 index 0000000..cd70d7a --- /dev/null +++ b/crates/std/patches/sys/args/mod.sed @@ -0,0 +1,4 @@ +# 55a \ target_os = "survos" => { \ +# mod survos; \ +# pub use survos::*; \ +# } diff --git a/crates/std/patches/sys/io/error/mod.sed b/crates/std/patches/sys/io/error/mod.sed new file mode 100644 index 0000000..3c6f879 --- /dev/null +++ b/crates/std/patches/sys/io/error/mod.sed @@ -0,0 +1 @@ +45a \ target_os = "survos", diff --git a/crates/std/patches/sys/random/mod.sed b/crates/std/patches/sys/random/mod.sed new file mode 100644 index 0000000..32c4012 --- /dev/null +++ b/crates/std/patches/sys/random/mod.sed @@ -0,0 +1,2 @@ +108a \target_os = "survos", +124a \target_os = "survos", diff --git a/crates/std/patches/sys/thread_local/mod.sed b/crates/std/patches/sys/thread_local/mod.sed new file mode 100644 index 0000000..d436d18 --- /dev/null +++ b/crates/std/patches/sys/thread_local/mod.sed @@ -0,0 +1,6 @@ +32a \ target_os = "survos", + +129a \ target_os = "survos" => { \ + // todo \ + pub(crate) fn enable() {} \ + } diff --git a/crates/std/src/collections/mod.rs b/crates/std/src/collections/mod.rs index d755d75..a17ef5a 100644 --- a/crates/std/src/collections/mod.rs +++ b/crates/std/src/collections/mod.rs @@ -3,4 +3,5 @@ use core::marker::PhantomData; pub struct HashMap { _phantom: PhantomData<(K, V, T)>, } -pub use alloc_crate::collections; +pub use alloc_crate::collections::BTreeMap; +pub use alloc_crate::collections::btree_map; diff --git a/crates/std/src/env.rs b/crates/std/src/env.rs index 469e4a8..98b2178 100644 --- a/crates/std/src/env.rs +++ b/crates/std/src/env.rs @@ -1,5 +1,1157 @@ -use crate::ffi::OsString; +//! Inspection and manipulation of the process's environment. +//! +//! This module contains functions to inspect various aspects such as +//! environment variables, process arguments, the current directory, and various +//! other important directories. +//! +//! There are several functions and structs in this module that have a +//! counterpart ending in `os`. Those ending in `os` will return an [`OsString`] +//! and those without will return a [`String`]. -pub fn var_os(s: &str) -> Option { - None +#![stable(feature = "env", since = "1.0.0")] + +use crate::error::Error; +use crate::ffi::{OsStr, OsString}; +use crate::num::NonZero; +use crate::ops::Try; +use crate::path::{Path, PathBuf}; +use crate::sys::{env as env_imp, os as os_imp}; +use crate::{array, fmt, io, sys}; + +/// Returns the current working directory as a [`PathBuf`]. +/// +/// # Platform-specific behavior +/// +/// This function [currently] corresponds to the `getcwd` function on Unix +/// and the `GetCurrentDirectoryW` function on Windows. +/// +/// [currently]: crate::io#platform-specific-behavior +/// +/// # Errors +/// +/// Returns an [`Err`] if the current working directory value is invalid. +/// Possible cases: +/// +/// * Current directory does not exist. +/// * There are insufficient permissions to access the current directory. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// fn main() -> std::io::Result<()> { +/// let path = env::current_dir()?; +/// println!("The current directory is {}", path.display()); +/// Ok(()) +/// } +/// ``` +#[doc(alias = "pwd")] +#[doc(alias = "getcwd")] +#[doc(alias = "GetCurrentDirectory")] +#[stable(feature = "env", since = "1.0.0")] +pub fn current_dir() -> io::Result { + os_imp::getcwd() +} + +/// Changes the current working directory to the specified path. +/// +/// # Platform-specific behavior +/// +/// This function [currently] corresponds to the `chdir` function on Unix +/// and the `SetCurrentDirectoryW` function on Windows. +/// +/// Returns an [`Err`] if the operation fails. +/// +/// [currently]: crate::io#platform-specific-behavior +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// use std::path::Path; +/// +/// let root = Path::new("/"); +/// assert!(env::set_current_dir(&root).is_ok()); +/// println!("Successfully changed working directory to {}!", root.display()); +/// ``` +#[doc(alias = "chdir", alias = "SetCurrentDirectory", alias = "SetCurrentDirectoryW")] +#[stable(feature = "env", since = "1.0.0")] +pub fn set_current_dir>(path: P) -> io::Result<()> { + os_imp::chdir(path.as_ref()) +} + +/// An iterator over a snapshot of the environment variables of this process. +/// +/// This structure is created by [`env::vars()`]. See its documentation for more. +/// +/// [`env::vars()`]: vars +#[stable(feature = "env", since = "1.0.0")] +pub struct Vars { + inner: VarsOs, +} + +/// An iterator over a snapshot of the environment variables of this process. +/// +/// This structure is created by [`env::vars_os()`]. See its documentation for more. +/// +/// [`env::vars_os()`]: vars_os +#[stable(feature = "env", since = "1.0.0")] +pub struct VarsOs { + inner: env_imp::Env, +} + +/// Returns an iterator of (variable, value) pairs of strings, for all the +/// environment variables of the current process. +/// +/// The returned iterator contains a snapshot of the process's environment +/// variables at the time of this invocation. Modifications to environment +/// variables afterwards will not be reflected in the returned iterator. +/// +/// # Panics +/// +/// While iterating, the returned iterator will panic if any key or value in the +/// environment is not valid unicode. If this is not desired, consider using +/// [`env::vars_os()`]. +/// +/// # Examples +/// +/// ``` +/// // Print all environment variables. +/// for (key, value) in std::env::vars() { +/// println!("{key}: {value}"); +/// } +/// ``` +/// +/// [`env::vars_os()`]: vars_os +#[must_use] +#[stable(feature = "env", since = "1.0.0")] +pub fn vars() -> Vars { + Vars { inner: vars_os() } +} + +/// Returns an iterator of (variable, value) pairs of OS strings, for all the +/// environment variables of the current process. +/// +/// The returned iterator contains a snapshot of the process's environment +/// variables at the time of this invocation. Modifications to environment +/// variables afterwards will not be reflected in the returned iterator. +/// +/// Note that the returned iterator will not check if the environment variables +/// are valid Unicode. If you want to panic on invalid UTF-8, +/// use the [`vars`] function instead. +/// +/// # Examples +/// +/// ``` +/// // Print all environment variables. +/// for (key, value) in std::env::vars_os() { +/// println!("{key:?}: {value:?}"); +/// } +/// ``` +#[must_use] +#[stable(feature = "env", since = "1.0.0")] +pub fn vars_os() -> VarsOs { + VarsOs { inner: env_imp::env() } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for Vars { + type Item = (String, String); + fn next(&mut self) -> Option<(String, String)> { + self.inner.next().map(|(a, b)| (a.into_string().unwrap(), b.into_string().unwrap())) + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Vars { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { inner: VarsOs { inner } } = self; + f.debug_struct("Vars").field("inner", inner).finish() + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for VarsOs { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for VarsOs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { inner } = self; + f.debug_struct("VarsOs").field("inner", inner).finish() + } +} + +/// Fetches the environment variable `key` from the current process. +/// +/// # Errors +/// +/// Returns [`VarError::NotPresent`] if: +/// - The variable is not set. +/// - The variable's name contains an equal sign or NUL (`'='` or `'\0'`). +/// +/// Returns [`VarError::NotUnicode`] if the variable's value is not valid +/// Unicode. If this is not desired, consider using [`var_os`]. +/// +/// Use [`env!`] or [`option_env!`] instead if you want to check environment +/// variables at compile time. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "HOME"; +/// match env::var(key) { +/// Ok(val) => println!("{key}: {val:?}"), +/// Err(e) => println!("couldn't interpret {key}: {e}"), +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn var>(key: K) -> Result { + _var(key.as_ref()) +} + +fn _var(key: &OsStr) -> Result { + match var_os(key) { + Some(s) => s.into_string().map_err(VarError::NotUnicode), + None => Err(VarError::NotPresent), + } +} + +/// Fetches the environment variable `key` from the current process, returning +/// [`None`] if the variable isn't set or if there is another error. +/// +/// It may return `None` if the environment variable's name contains +/// the equal sign character (`=`) or the NUL character. +/// +/// Note that this function will not check if the environment variable +/// is valid Unicode. If you want to have an error on invalid UTF-8, +/// use the [`var`] function instead. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "HOME"; +/// match env::var_os(key) { +/// Some(val) => println!("{key}: {val:?}"), +/// None => println!("{key} is not defined in the environment.") +/// } +/// ``` +/// +/// If expecting a delimited variable (such as `PATH`), [`split_paths`] +/// can be used to separate items. +#[must_use] +#[stable(feature = "env", since = "1.0.0")] +pub fn var_os>(key: K) -> Option { + _var_os(key.as_ref()) +} + +fn _var_os(key: &OsStr) -> Option { + env_imp::getenv(key) +} + +/// The error type for operations interacting with environment variables. +/// Possibly returned from [`env::var()`]. +/// +/// [`env::var()`]: var +#[derive(Debug, PartialEq, Eq, Clone)] +#[stable(feature = "env", since = "1.0.0")] +pub enum VarError { + /// The specified environment variable was not present in the current + /// process's environment. + #[stable(feature = "env", since = "1.0.0")] + NotPresent, + + /// The specified environment variable was found, but it did not contain + /// valid unicode data. The found data is returned as a payload of this + /// variant. + #[stable(feature = "env", since = "1.0.0")] + NotUnicode(#[stable(feature = "env", since = "1.0.0")] OsString), +} + +#[stable(feature = "env", since = "1.0.0")] +impl fmt::Display for VarError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + VarError::NotPresent => write!(f, "environment variable not found"), + VarError::NotUnicode(ref s) => { + write!(f, "environment variable was not valid unicode: {:?}", s) + } + } + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Error for VarError {} + +/// Sets the environment variable `key` to the value `value` for the currently running +/// process. +/// +/// # Safety +/// +/// This function is safe to call in a single-threaded program. +/// +/// This function is also always safe to call on Windows, in single-threaded +/// and multi-threaded programs. +/// +/// In multi-threaded programs on other operating systems, the only safe option is +/// to not use `set_var` or `remove_var` at all. +/// +/// The exact requirement is: you +/// must ensure that there are no other threads concurrently writing or +/// *reading*(!) the environment through functions or global variables other +/// than the ones in this module. The problem is that these operating systems +/// do not provide a thread-safe way to read the environment, and most C +/// libraries, including libc itself, do not advertise which functions read +/// from the environment. Even functions from the Rust standard library may +/// read the environment without going through this module, e.g. for DNS +/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about +/// which functions may read from the environment in future versions of a +/// library. All this makes it not practically possible for you to guarantee +/// that no other thread will read the environment, so the only safe option is +/// to not use `set_var` or `remove_var` in multi-threaded programs at all. +/// +/// Discussion of this unsafety on Unix may be found in: +/// +/// - [Austin Group Bugzilla (for POSIX)](https://austingroupbugs.net/view.php?id=188) +/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) +/// +/// To pass an environment variable to a child process, you can instead use [`Command::env`]. +/// +/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env`]: crate::process::Command::env +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign `'='` +/// or the NUL character `'\0'`, or when `value` contains the NUL character. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "KEY"; +/// unsafe { +/// env::set_var(key, "VALUE"); +/// } +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// ``` +#[rustc_deprecated_safe_2024( + audit_that = "the environment access only happens in single-threaded code" +)] +#[stable(feature = "env", since = "1.0.0")] +pub unsafe fn set_var, V: AsRef>(key: K, value: V) { + let (key, value) = (key.as_ref(), value.as_ref()); + unsafe { env_imp::setenv(key, value) }.unwrap_or_else(|e| { + panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}") + }) +} + +/// Removes an environment variable from the environment of the currently running process. +/// +/// # Safety +/// +/// This function is safe to call in a single-threaded program. +/// +/// This function is also always safe to call on Windows, in single-threaded +/// and multi-threaded programs. +/// +/// In multi-threaded programs on other operating systems, the only safe option is +/// to not use `set_var` or `remove_var` at all. +/// +/// The exact requirement is: you +/// must ensure that there are no other threads concurrently writing or +/// *reading*(!) the environment through functions or global variables other +/// than the ones in this module. The problem is that these operating systems +/// do not provide a thread-safe way to read the environment, and most C +/// libraries, including libc itself, do not advertise which functions read +/// from the environment. Even functions from the Rust standard library may +/// read the environment without going through this module, e.g. for DNS +/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about +/// which functions may read from the environment in future versions of a +/// library. All this makes it not practically possible for you to guarantee +/// that no other thread will read the environment, so the only safe option is +/// to not use `set_var` or `remove_var` in multi-threaded programs at all. +/// +/// Discussion of this unsafety on Unix may be found in: +/// +/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) +/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) +/// +/// To prevent a child process from inheriting an environment variable, you can +/// instead use [`Command::env_remove`] or [`Command::env_clear`]. +/// +/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env_remove`]: crate::process::Command::env_remove +/// [`Command::env_clear`]: crate::process::Command::env_clear +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign +/// `'='` or the NUL character `'\0'`, or when the value contains the NUL +/// character. +/// +/// # Examples +/// +/// ```no_run +/// use std::env; +/// +/// let key = "KEY"; +/// unsafe { +/// env::set_var(key, "VALUE"); +/// } +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// +/// unsafe { +/// env::remove_var(key); +/// } +/// assert!(env::var(key).is_err()); +/// ``` +#[rustc_deprecated_safe_2024( + audit_that = "the environment access only happens in single-threaded code" +)] +#[stable(feature = "env", since = "1.0.0")] +pub unsafe fn remove_var>(key: K) { + let key = key.as_ref(); + unsafe { env_imp::unsetenv(key) } + .unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}")) +} + +/// An iterator that splits an environment variable into paths according to +/// platform-specific conventions. +/// +/// The iterator element type is [`PathBuf`]. +/// +/// This structure is created by [`env::split_paths()`]. See its +/// documentation for more. +/// +/// [`env::split_paths()`]: split_paths +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "env", since = "1.0.0")] +pub struct SplitPaths<'a> { + inner: os_imp::SplitPaths<'a>, +} + +/// Parses input according to platform conventions for the `PATH` +/// environment variable. +/// +/// Returns an iterator over the paths contained in `unparsed`. The iterator +/// element type is [`PathBuf`]. +/// +/// On most Unix platforms, the separator is `:` and on Windows it is `;`. This +/// also performs unquoting on Windows. +/// +/// [`join_paths`] can be used to recombine elements. +/// +/// # Panics +/// +/// This will panic on systems where there is no delimited `PATH` variable, +/// such as UEFI. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// let key = "PATH"; +/// match env::var_os(key) { +/// Some(paths) => { +/// for path in env::split_paths(&paths) { +/// println!("'{}'", path.display()); +/// } +/// } +/// None => println!("{key} is not defined in the environment.") +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn split_paths + ?Sized>(unparsed: &T) -> SplitPaths<'_> { + SplitPaths { inner: os_imp::split_paths(unparsed.as_ref()) } +} + +#[stable(feature = "env", since = "1.0.0")] +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for SplitPaths<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitPaths").finish_non_exhaustive() + } +} + +/// The error type for operations on the `PATH` variable. Possibly returned from +/// [`env::join_paths()`]. +/// +/// [`env::join_paths()`]: join_paths +#[derive(Debug)] +#[stable(feature = "env", since = "1.0.0")] +pub struct JoinPathsError { + inner: os_imp::JoinPathsError, +} + +/// Joins a collection of [`Path`]s appropriately for the `PATH` +/// environment variable. +/// +/// # Errors +/// +/// Returns an [`Err`] (containing an error message) if one of the input +/// [`Path`]s contains an invalid character for constructing the `PATH` +/// variable (a double quote on Windows or a colon on Unix or semicolon on +/// UEFI), or if the system does not have a `PATH`-like variable (e.g. WASI). +/// +/// # Examples +/// +/// Joining paths on a Unix-like platform: +/// +/// ``` +/// use std::env; +/// use std::ffi::OsString; +/// use std::path::Path; +/// +/// fn main() -> Result<(), env::JoinPathsError> { +/// # if cfg!(unix) { +/// let paths = [Path::new("/bin"), Path::new("/usr/bin")]; +/// let path_os_string = env::join_paths(paths.iter())?; +/// assert_eq!(path_os_string, OsString::from("/bin:/usr/bin")); +/// # } +/// Ok(()) +/// } +/// ``` +/// +/// Joining a path containing a colon on a Unix-like platform results in an +/// error: +/// +/// ``` +/// # if cfg!(unix) { +/// use std::env; +/// use std::path::Path; +/// +/// let paths = [Path::new("/bin"), Path::new("/usr/bi:n")]; +/// assert!(env::join_paths(paths.iter()).is_err()); +/// # } +/// ``` +/// +/// Using `env::join_paths()` with [`env::split_paths()`] to append an item to +/// the `PATH` environment variable: +/// +/// ``` +/// use std::env; +/// use std::path::PathBuf; +/// +/// fn main() -> Result<(), env::JoinPathsError> { +/// if let Some(path) = env::var_os("PATH") { +/// let mut paths = env::split_paths(&path).collect::>(); +/// paths.push(PathBuf::from("/home/xyz/bin")); +/// let new_path = env::join_paths(paths)?; +/// unsafe { env::set_var("PATH", &new_path); } +/// } +/// +/// Ok(()) +/// } +/// ``` +/// +/// [`env::split_paths()`]: split_paths +#[stable(feature = "env", since = "1.0.0")] +pub fn join_paths(paths: I) -> Result +where + I: IntoIterator, + T: AsRef, +{ + os_imp::join_paths(paths.into_iter()).map_err(|e| JoinPathsError { inner: e }) +} + +#[stable(feature = "env", since = "1.0.0")] +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl Error for JoinPathsError { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + self.inner.description() + } +} + +/// Returns the path of the current user's home directory if known. +/// +/// This may return `None` if getting the directory fails or if the platform does not have user home directories. +/// +/// For storing user data and configuration it is often preferable to use more specific directories. +/// For example, [XDG Base Directories] on Unix or the `LOCALAPPDATA` and `APPDATA` environment variables on Windows. +/// +/// [XDG Base Directories]: https://specifications.freedesktop.org/basedir-spec/latest/ +/// +/// # Unix +/// +/// - Returns the value of the 'HOME' environment variable if it is set +/// (and not an empty string). +/// - Otherwise, it tries to determine the home directory by invoking the `getpwuid_r` function +/// using the UID of the current user. An empty home directory field returned from the +/// `getpwuid_r` function is considered to be a valid value. +/// - Returns `None` if the current user has no entry in the /etc/passwd file. +/// +/// # Windows +/// +/// - Returns the value of the 'USERPROFILE' environment variable if it is set, and is not an empty string. +/// - Otherwise, [`GetUserProfileDirectory`][msdn] is used to return the path. This may change in the future. +/// +/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectorya +/// +/// In UWP (Universal Windows Platform) targets this function is unimplemented and always returns `None`. +/// +/// Before Rust 1.85.0, this function used to return the value of the 'HOME' environment variable +/// on Windows, which in Cygwin or Mingw environments could return non-standard paths like `/home/you` +/// instead of `C:\Users\you`. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// match env::home_dir() { +/// Some(path) => println!("Your home directory, probably: {}", path.display()), +/// None => println!("Impossible to get your home dir!"), +/// } +/// ``` +#[must_use] +#[stable(feature = "env", since = "1.0.0")] +pub fn home_dir() -> Option { + os_imp::home_dir() +} + +/// Returns the path of a temporary directory. +/// +/// The temporary directory may be shared among users, or between processes +/// with different privileges; thus, the creation of any files or directories +/// in the temporary directory must use a secure method to create a uniquely +/// named file. Creating a file or directory with a fixed or predictable name +/// may result in "insecure temporary file" security vulnerabilities. Consider +/// using a crate that securely creates temporary files or directories. +/// +/// Note that the returned value may be a symbolic link, not a directory. +/// +/// # Platform-specific behavior +/// +/// On Unix, returns the value of the `TMPDIR` environment variable if it is +/// set, otherwise the value is OS-specific: +/// - On Android, there is no global temporary folder (it is usually allocated +/// per-app), it will return the application's cache dir if the program runs +/// in application's namespace and system version is Android 13 (or above), or +/// `/data/local/tmp` otherwise. +/// - On Darwin-based OSes (macOS, iOS, etc) it returns the directory provided +/// by `confstr(_CS_DARWIN_USER_TEMP_DIR, ...)`, as recommended by [Apple's +/// security guidelines][appledoc]. +/// - On all other unix-based OSes, it returns `/tmp`. +/// +/// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] / +/// [`GetTempPath`][GetTempPath], which this function uses internally. +/// +/// Note that, this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +/// [GetTempPath2]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a +/// [GetTempPath]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha +/// [appledoc]: https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html#//apple_ref/doc/uid/TP40002585-SW10 +/// +/// ```no_run +/// use std::env; +/// +/// fn main() { +/// let dir = env::temp_dir(); +/// println!("Temporary directory: {}", dir.display()); +/// } +/// ``` +#[must_use] +#[doc(alias = "GetTempPath", alias = "GetTempPath2")] +#[stable(feature = "env", since = "1.0.0")] +pub fn temp_dir() -> PathBuf { + os_imp::temp_dir() +} + +/// Returns the full filesystem path of the current running executable. +/// +/// # Platform-specific behavior +/// +/// If the executable was invoked through a symbolic link, some platforms will +/// return the path of the symbolic link and other platforms will return the +/// path of the symbolic link’s target. +/// +/// If the executable is renamed while it is running, platforms may return the +/// path at the time it was loaded instead of the new path. +/// +/// # Errors +/// +/// Acquiring the path of the current executable is a platform-specific operation +/// that can fail for a good number of reasons. Some errors can include, but not +/// be limited to, filesystem operations failing or general syscall failures. +/// +/// # Security +/// +/// The output of this function must be treated with care to avoid security +/// vulnerabilities, particularly in processes that run with privileges higher +/// than the user, such as setuid or setgid programs. +/// +/// For example, on some Unix platforms, the result is calculated by +/// searching `$PATH` for an executable matching `argv[0]`, but both the +/// environment and arguments can be be set arbitrarily by the user who +/// invokes the program. +/// +/// On Linux, if `fs.secure_hardlinks` is not set, an attacker who can +/// create hardlinks to the executable may be able to cause this function +/// to return an attacker-controlled path, which they later replace with +/// a different program. +/// +/// This list of illustrative example attacks is not exhaustive. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// match env::current_exe() { +/// Ok(exe_path) => println!("Path of this executable is: {}", +/// exe_path.display()), +/// Err(e) => println!("failed to get current exe path: {e}"), +/// }; +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn current_exe() -> io::Result { + os_imp::current_exe() +} + +/// An iterator over the arguments of a process, yielding a [`String`] value for +/// each argument. +/// +/// This struct is created by [`env::args()`]. See its documentation +/// for more. +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and might not even exist. This means this property +/// should not be relied upon for security purposes. +/// +/// [`env::args()`]: args +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "env", since = "1.0.0")] +pub struct Args { + inner: ArgsOs, +} + +/// An iterator over the arguments of a process, yielding an [`OsString`] value +/// for each argument. +/// +/// This struct is created by [`env::args_os()`]. See its documentation +/// for more. +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and might not even exist. This means this property +/// should not be relied upon for security purposes. +/// +/// [`env::args_os()`]: args_os +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "env", since = "1.0.0")] +pub struct ArgsOs { + inner: sys::args::Args, +} + +/// Returns the arguments that this program was started with (normally passed +/// via the command line). +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and might not even exist. This means this property should +/// not be relied upon for security purposes. +/// +/// On Unix systems the shell usually expands unquoted arguments with glob patterns +/// (such as `*` and `?`). On Windows this is not done, and such arguments are +/// passed as-is. +/// +/// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`. +/// glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard +/// extension. This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it +/// does on macOS and Windows. +/// +/// # Panics +/// +/// The returned iterator will panic during iteration if any argument to the +/// process is not valid Unicode. If this is not desired, +/// use the [`args_os`] function instead. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// // Prints each argument on a separate line +/// for argument in env::args() { +/// println!("{argument}"); +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn args() -> Args { + Args { inner: args_os() } +} + +/// Returns the arguments that this program was started with (normally passed +/// via the command line). +/// +/// The first element is traditionally the path of the executable, but it can be +/// set to arbitrary text, and might not even exist. This means this property should +/// not be relied upon for security purposes. +/// +/// On Unix systems the shell usually expands unquoted arguments with glob patterns +/// (such as `*` and `?`). On Windows this is not done, and such arguments are +/// passed as-is. +/// +/// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`. +/// glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard +/// extension. This allows `std::env::args_os` to work even in a `cdylib` or `staticlib`, as it +/// does on macOS and Windows. +/// +/// Note that the returned iterator will not check if the arguments to the +/// process are valid Unicode. If you want to panic on invalid UTF-8, +/// use the [`args`] function instead. +/// +/// # Examples +/// +/// ``` +/// use std::env; +/// +/// // Prints each argument on a separate line +/// for argument in env::args_os() { +/// println!("{argument:?}"); +/// } +/// ``` +#[stable(feature = "env", since = "1.0.0")] +pub fn args_os() -> ArgsOs { + ArgsOs { inner: sys::args::args() } +} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Send for Args {} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Sync for Args {} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for Args { + type Item = String; + + fn next(&mut self) -> Option { + self.inner.next().map(|s| s.into_string().unwrap()) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + // Methods which skip args cannot simply delegate to the inner iterator, + // because `env::args` states that we will "panic during iteration if any + // argument to the process is not valid Unicode". + // + // This offers two possible interpretations: + // - a skipped argument is never encountered "during iteration" + // - even a skipped argument is encountered "during iteration" + // + // As a panic can be observed, we err towards validating even skipped + // arguments for now, though this is not explicitly promised by the API. +} + +#[stable(feature = "env", since = "1.0.0")] +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +#[stable(feature = "env_iterators", since = "1.12.0")] +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.inner.next_back().map(|s| s.into_string().unwrap()) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { inner: ArgsOs { inner } } = self; + f.debug_struct("Args").field("inner", inner).finish() + } +} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Send for ArgsOs {} + +#[stable(feature = "env_unimpl_send_sync", since = "1.26.0")] +impl !Sync for ArgsOs {} + +#[stable(feature = "env", since = "1.0.0")] +impl Iterator for ArgsOs { + type Item = OsString; + + #[inline] + fn next(&mut self) -> Option { + self.inner.next() + } + + #[inline] + fn next_chunk( + &mut self, + ) -> Result<[OsString; N], array::IntoIter> { + self.inner.next_chunk() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.inner.len() + } + + #[inline] + fn last(self) -> Option { + self.inner.last() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.inner.advance_by(n) + } + + #[inline] + fn try_fold(&mut self, init: B, f: F) -> R + where + F: FnMut(B, Self::Item) -> R, + R: Try, + { + self.inner.try_fold(init, f) + } + + #[inline] + fn fold(self, init: B, f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.inner.fold(init, f) + } +} + +#[stable(feature = "env", since = "1.0.0")] +impl ExactSizeIterator for ArgsOs { + #[inline] + fn len(&self) -> usize { + self.inner.len() + } + + #[inline] + fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +#[stable(feature = "env_iterators", since = "1.12.0")] +impl DoubleEndedIterator for ArgsOs { + #[inline] + fn next_back(&mut self) -> Option { + self.inner.next_back() + } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.inner.advance_back_by(n) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ArgsOs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { inner } = self; + f.debug_struct("ArgsOs").field("inner", inner).finish() + } +} + +/// Constants associated with the current target +#[stable(feature = "env", since = "1.0.0")] +pub mod consts { + use crate::sys::env_consts::os; + + /// A string describing the architecture of the CPU that is currently in use. + /// An example value may be: `"x86"`, `"arm"` or `"riscv64"`. + /// + ///
Full list of possible values + /// + /// * `"x86"` + /// * `"x86_64"` + /// * `"arm"` + /// * `"aarch64"` + /// * `"m68k"` + /// * `"mips"` + /// * `"mips32r6"` + /// * `"mips64"` + /// * `"mips64r6"` + /// * `"csky"` + /// * `"powerpc"` + /// * `"powerpc64"` + /// * `"riscv32"` + /// * `"riscv64"` + /// * `"s390x"` + /// * `"sparc"` + /// * `"sparc64"` + /// * `"hexagon"` + /// * `"loongarch32"` + /// * `"loongarch64"` + /// + ///
+ #[stable(feature = "env", since = "1.0.0")] + pub const ARCH: &str = env!("STD_ENV_ARCH"); + + /// A string describing the family of the operating system. + /// An example value may be: `"unix"`, or `"windows"`. + /// + /// This value may be an empty string if the family is unknown. + /// + ///
Full list of possible values + /// + /// * `"unix"` + /// * `"windows"` + /// * `"itron"` + /// * `"wasm"` + /// * `""` + /// + ///
+ #[stable(feature = "env", since = "1.0.0")] + pub const FAMILY: &str = os::FAMILY; + + /// A string describing the specific operating system in use. + /// An example value may be: `"linux"`, or `"freebsd"`. + /// + ///
Full list of possible values + /// + /// * `"linux"` + /// * `"windows"` + /// * `"macos"` + /// * `"android"` + /// * `"ios"` + /// * `"openbsd"` + /// * `"freebsd"` + /// * `"netbsd"` + /// * `"wasi"` + /// * `"hermit"` + /// * `"aix"` + /// * `"apple"` + /// * `"dragonfly"` + /// * `"emscripten"` + /// * `"espidf"` + /// * `"fortanix"` + /// * `"uefi"` + /// * `"fuchsia"` + /// * `"haiku"` + /// * `"hermit"` + /// * `"watchos"` + /// * `"visionos"` + /// * `"tvos"` + /// * `"horizon"` + /// * `"hurd"` + /// * `"illumos"` + /// * `"l4re"` + /// * `"nto"` + /// * `"redox"` + /// * `"solaris"` + /// * `"solid_asp3"` + /// * `"vexos"` + /// * `"vita"` + /// * `"vxworks"` + /// * `"xous"` + /// + ///
+ #[stable(feature = "env", since = "1.0.0")] + pub const OS: &str = os::OS; + + /// Specifies the filename prefix, if any, used for shared libraries on this platform. + /// This is either `"lib"` or an empty string. (`""`). + #[stable(feature = "env", since = "1.0.0")] + pub const DLL_PREFIX: &str = os::DLL_PREFIX; + + /// Specifies the filename suffix, if any, used for shared libraries on this platform. + /// An example value may be: `".so"`, `".elf"`, or `".dll"`. + /// + /// The possible values are identical to those of [`DLL_EXTENSION`], but with the leading period included. + #[stable(feature = "env", since = "1.0.0")] + pub const DLL_SUFFIX: &str = os::DLL_SUFFIX; + + /// Specifies the file extension, if any, used for shared libraries on this platform that goes after the dot. + /// An example value may be: `"so"`, `"elf"`, or `"dll"`. + /// + ///
Full list of possible values + /// + /// * `"so"` + /// * `"dylib"` + /// * `"dll"` + /// * `"sgxs"` + /// * `"a"` + /// * `"elf"` + /// * `"wasm"` + /// * `""` (an empty string) + /// + ///
+ #[stable(feature = "env", since = "1.0.0")] + pub const DLL_EXTENSION: &str = os::DLL_EXTENSION; + + /// Specifies the filename suffix, if any, used for executable binaries on this platform. + /// An example value may be: `".exe"`, or `".efi"`. + /// + /// The possible values are identical to those of [`EXE_EXTENSION`], but with the leading period included. + #[stable(feature = "env", since = "1.0.0")] + pub const EXE_SUFFIX: &str = os::EXE_SUFFIX; + + /// Specifies the file extension, if any, used for executable binaries on this platform. + /// An example value may be: `"exe"`, or an empty string (`""`). + /// + ///
Full list of possible values + /// + /// * `"bin"` + /// * `"exe"` + /// * `"efi"` + /// * `"js"` + /// * `"sgxs"` + /// * `"elf"` + /// * `"wasm"` + /// * `""` (an empty string) + /// + ///
+ #[stable(feature = "env", since = "1.0.0")] + pub const EXE_EXTENSION: &str = os::EXE_EXTENSION; } diff --git a/crates/std/src/io.rs b/crates/std/src/io.rs index 11fb8dc..70095b3 100644 --- a/crates/std/src/io.rs +++ b/crates/std/src/io.rs @@ -9,8 +9,8 @@ pub mod copy; pub mod cursor; pub mod impls; pub mod pipe; -pub mod stdio; pub mod prelude; +pub mod stdio; pub mod util; use crate::mem::{MaybeUninit, take}; @@ -18,9 +18,15 @@ use crate::ops::{Deref, DerefMut}; use crate::{cmp, fmt, slice, str, sys}; #[unstable(feature = "read_buf", issue = "78485")] pub use core::io::{BorrowedBuf, BorrowedCursor}; -pub use cursor::Cursor; use core::slice::memchr; +pub use cursor::Cursor; + +pub use self::stdio::{ + Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout, +}; +pub use self::pipe::{PipeReader, PipeWriter}; pub use stdio::try_set_output_capture; +pub(crate) use stdio::attempt_print_to_stderr; use crate::fs::File; use io::IoBase; @@ -29,7 +35,7 @@ use io::IoBase; // pub use io::SeekFrom; // pub use io::Write; -pub struct Stdin; +// pub struct Stdin; impl IoBase for Stdin { type Error = (); @@ -41,9 +47,9 @@ impl io::Read for Stdin { } } -pub fn stdin() -> Stdin { - Stdin -} +// pub fn stdin() -> Stdin { +// Stdin +// } // Part took from the real std diff --git a/crates/std/src/io/stdio.rs b/crates/std/src/io/stdio.rs index 689d0b5..2d80fe4 100644 --- a/crates/std/src/io/stdio.rs +++ b/crates/std/src/io/stdio.rs @@ -1,7 +1,7 @@ #![cfg_attr(test, allow(unused))] -// #[cfg(test)] -// mod tests; +#[cfg(test)] +mod tests; use crate::cell::{Cell, RefCell}; use crate::fmt; @@ -18,1105 +18,1105 @@ use crate::thread::AccessError; type LocalStream = Arc>>; -// thread_local! { -// /// Used by the test crate to capture the output of the print macros and panics. -// static OUTPUT_CAPTURE: Cell> = const { -// Cell::new(None) -// } -// } - -// /// Flag to indicate OUTPUT_CAPTURE is used. -// /// -// /// If it is None and was never set on any thread, this flag is set to false, -// /// and OUTPUT_CAPTURE can be safely ignored on all threads, saving some time -// /// and memory registering an unused thread local. -// /// -// /// Note about memory ordering: This contains information about whether a -// /// thread local variable might be in use. Although this is a global flag, the -// /// memory ordering between threads does not matter: we only want this flag to -// /// have a consistent order between set_output_capture and print_to *within -// /// the same thread*. Within the same thread, things always have a perfectly -// /// consistent order. So Ordering::Relaxed is fine. -// static OUTPUT_CAPTURE_USED: Atomic = AtomicBool::new(false); - -// /// A handle to a raw instance of the standard input stream of this process. -// /// -// /// This handle is not synchronized or buffered in any fashion. Constructed via -// /// the `std::io::stdio::stdin_raw` function. -// struct StdinRaw(stdio::Stdin); - -// /// A handle to a raw instance of the standard output stream of this process. -// /// -// /// This handle is not synchronized or buffered in any fashion. Constructed via -// /// the `std::io::stdio::stdout_raw` function. -// struct StdoutRaw(stdio::Stdout); - -// /// A handle to a raw instance of the standard output stream of this process. -// /// -// /// This handle is not synchronized or buffered in any fashion. Constructed via -// /// the `std::io::stdio::stderr_raw` function. -// struct StderrRaw(stdio::Stderr); - -// /// Constructs a new raw handle to the standard input of this process. -// /// -// /// The returned handle does not interact with any other handles created nor -// /// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin` -// /// handles is **not** available to raw handles returned from this function. -// /// -// /// The returned handle has no external synchronization or buffering. -// #[unstable(feature = "libstd_sys_internals", issue = "none")] -// const fn stdin_raw() -> StdinRaw { -// StdinRaw(stdio::Stdin::new()) -// } - -// /// Constructs a new raw handle to the standard output stream of this process. -// /// -// /// The returned handle does not interact with any other handles created nor -// /// handles returned by `std::io::stdout`. Note that data is buffered by the -// /// `std::io::stdout` handles so writes which happen via this raw handle may -// /// appear before previous writes. -// /// -// /// The returned handle has no external synchronization or buffering layered on -// /// top. -// #[unstable(feature = "libstd_sys_internals", issue = "none")] -// const fn stdout_raw() -> StdoutRaw { -// StdoutRaw(stdio::Stdout::new()) -// } - -// /// Constructs a new raw handle to the standard error stream of this process. -// /// -// /// The returned handle does not interact with any other handles created nor -// /// handles returned by `std::io::stderr`. -// /// -// /// The returned handle has no external synchronization or buffering layered on -// /// top. -// #[unstable(feature = "libstd_sys_internals", issue = "none")] -// const fn stderr_raw() -> StderrRaw { -// StderrRaw(stdio::Stderr::new()) -// } - -// impl Read for StdinRaw { -// fn read(&mut self, buf: &mut [u8]) -> io::Result { -// handle_ebadf(self.0.read(buf), || Ok(0)) -// } - -// fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { -// handle_ebadf(self.0.read_buf(buf), || Ok(())) -// } - -// fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { -// handle_ebadf(self.0.read_vectored(bufs), || Ok(0)) -// } - -// #[inline] -// fn is_read_vectored(&self) -> bool { -// self.0.is_read_vectored() -// } - -// fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { -// if buf.is_empty() { -// return Ok(()); -// } -// handle_ebadf(self.0.read_exact(buf), || Err(io::Error::READ_EXACT_EOF)) -// } - -// fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { -// if buf.capacity() == 0 { -// return Ok(()); -// } -// handle_ebadf(self.0.read_buf_exact(buf), || Err(io::Error::READ_EXACT_EOF)) -// } - -// fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { -// handle_ebadf(self.0.read_to_end(buf), || Ok(0)) -// } - -// fn read_to_string(&mut self, buf: &mut String) -> io::Result { -// handle_ebadf(self.0.read_to_string(buf), || Ok(0)) -// } -// } - -// impl Write for StdoutRaw { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// handle_ebadf(self.0.write(buf), || Ok(buf.len())) -// } - -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// let total = || Ok(bufs.iter().map(|b| b.len()).sum()); -// handle_ebadf(self.0.write_vectored(bufs), total) -// } - -// #[inline] -// fn is_write_vectored(&self) -> bool { -// self.0.is_write_vectored() -// } - -// fn flush(&mut self) -> io::Result<()> { -// handle_ebadf(self.0.flush(), || Ok(())) -// } - -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// handle_ebadf(self.0.write_all(buf), || Ok(())) -// } - -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) -// } - -// fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { -// handle_ebadf(self.0.write_fmt(fmt), || Ok(())) -// } -// } - -// impl Write for StderrRaw { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// handle_ebadf(self.0.write(buf), || Ok(buf.len())) -// } - -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// let total = || Ok(bufs.iter().map(|b| b.len()).sum()); -// handle_ebadf(self.0.write_vectored(bufs), total) -// } - -// #[inline] -// fn is_write_vectored(&self) -> bool { -// self.0.is_write_vectored() -// } - -// fn flush(&mut self) -> io::Result<()> { -// handle_ebadf(self.0.flush(), || Ok(())) -// } - -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// handle_ebadf(self.0.write_all(buf), || Ok(())) -// } - -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) -// } - -// fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { -// handle_ebadf(self.0.write_fmt(fmt), || Ok(())) -// } -// } - -// fn handle_ebadf(r: io::Result, default: impl FnOnce() -> io::Result) -> io::Result { -// match r { -// Err(ref e) if stdio::is_ebadf(e) => default(), -// r => r, -// } -// } - -// /// A handle to the standard input stream of a process. -// /// -// /// Each handle is a shared reference to a global buffer of input data to this -// /// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods -// /// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect -// /// to other reads. -// /// -// /// This handle implements the `Read` trait, but beware that concurrent reads -// /// of `Stdin` must be executed with care. -// /// -// /// Created by the [`io::stdin`] method. -// /// -// /// [`io::stdin`]: stdin -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// /// -// /// # Examples -// /// -// /// ```no_run -// /// use std::io; -// /// -// /// fn main() -> io::Result<()> { -// /// let mut buffer = String::new(); -// /// let stdin = io::stdin(); // We get `Stdin` here. -// /// stdin.read_line(&mut buffer)?; -// /// Ok(()) -// /// } -// /// ``` -// #[stable(feature = "rust1", since = "1.0.0")] -// #[cfg_attr(not(test), rustc_diagnostic_item = "Stdin")] -// pub struct Stdin { -// inner: &'static Mutex>, -// } - -// /// A locked reference to the [`Stdin`] handle. -// /// -// /// This handle implements both the [`Read`] and [`BufRead`] traits, and -// /// is constructed via the [`Stdin::lock`] method. -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// /// -// /// # Examples -// /// -// /// ```no_run -// /// use std::io::{self, BufRead}; -// /// -// /// fn main() -> io::Result<()> { -// /// let mut buffer = String::new(); -// /// let stdin = io::stdin(); // We get `Stdin` here. -// /// { -// /// let mut handle = stdin.lock(); // We get `StdinLock` here. -// /// handle.read_line(&mut buffer)?; -// /// } // `StdinLock` is dropped here. -// /// Ok(()) -// /// } -// /// ``` -// #[must_use = "if unused stdin will immediately unlock"] -// #[stable(feature = "rust1", since = "1.0.0")] -// pub struct StdinLock<'a> { -// inner: MutexGuard<'a, BufReader>, -// } - -// /// Constructs a new handle to the standard input of the current process. -// /// -// /// Each handle returned is a reference to a shared global buffer whose access -// /// is synchronized via a mutex. If you need more explicit control over -// /// locking, see the [`Stdin::lock`] method. -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// /// -// /// # Examples -// /// -// /// Using implicit synchronization: -// /// -// /// ```no_run -// /// use std::io; -// /// -// /// fn main() -> io::Result<()> { -// /// let mut buffer = String::new(); -// /// io::stdin().read_line(&mut buffer)?; -// /// Ok(()) -// /// } -// /// ``` -// /// -// /// Using explicit synchronization: -// /// -// /// ```no_run -// /// use std::io::{self, BufRead}; -// /// -// /// fn main() -> io::Result<()> { -// /// let mut buffer = String::new(); -// /// let stdin = io::stdin(); -// /// let mut handle = stdin.lock(); -// /// -// /// handle.read_line(&mut buffer)?; -// /// Ok(()) -// /// } -// /// ``` -// #[must_use] -// #[stable(feature = "rust1", since = "1.0.0")] -// pub fn stdin() -> Stdin { -// static INSTANCE: OnceLock>> = OnceLock::new(); -// Stdin { -// inner: INSTANCE.get_or_init(|| { -// Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin_raw())) -// }), -// } -// } - -// impl Stdin { -// /// Locks this handle to the standard input stream, returning a readable -// /// guard. -// /// -// /// The lock is released when the returned lock goes out of scope. The -// /// returned guard also implements the [`Read`] and [`BufRead`] traits for -// /// accessing the underlying data. -// /// -// /// # Examples -// /// -// /// ```no_run -// /// use std::io::{self, BufRead}; -// /// -// /// fn main() -> io::Result<()> { -// /// let mut buffer = String::new(); -// /// let stdin = io::stdin(); -// /// let mut handle = stdin.lock(); -// /// -// /// handle.read_line(&mut buffer)?; -// /// Ok(()) -// /// } -// /// ``` -// #[stable(feature = "rust1", since = "1.0.0")] -// pub fn lock(&self) -> StdinLock<'static> { -// // Locks this handle with 'static lifetime. This depends on the -// // implementation detail that the underlying `Mutex` is static. -// StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } -// } - -// /// Locks this handle and reads a line of input, appending it to the specified buffer. -// /// -// /// For detailed semantics of this method, see the documentation on -// /// [`BufRead::read_line`]. In particular: -// /// * Previous content of the buffer will be preserved. To avoid appending -// /// to the buffer, you need to [`clear`] it first. -// /// * The trailing newline character, if any, is included in the buffer. -// /// -// /// [`clear`]: String::clear -// /// -// /// # Examples -// /// -// /// ```no_run -// /// use std::io; -// /// -// /// let mut input = String::new(); -// /// match io::stdin().read_line(&mut input) { -// /// Ok(n) => { -// /// println!("{n} bytes read"); -// /// println!("{input}"); -// /// } -// /// Err(error) => println!("error: {error}"), -// /// } -// /// ``` -// /// -// /// You can run the example one of two ways: -// /// -// /// - Pipe some text to it, e.g., `printf foo | path/to/executable` -// /// - Give it text interactively by running the executable directly, -// /// in which case it will wait for the Enter key to be pressed before -// /// continuing -// #[stable(feature = "rust1", since = "1.0.0")] -// #[rustc_confusables("get_line")] -// pub fn read_line(&self, buf: &mut String) -> io::Result { -// self.lock().read_line(buf) -// } - -// /// Consumes this handle and returns an iterator over input lines. -// /// -// /// For detailed semantics of this method, see the documentation on -// /// [`BufRead::lines`]. -// /// -// /// # Examples -// /// -// /// ```no_run -// /// use std::io; -// /// -// /// let lines = io::stdin().lines(); -// /// for line in lines { -// /// println!("got a line: {}", line.unwrap()); -// /// } -// /// ``` -// #[must_use = "`self` will be dropped if the result is not used"] -// #[stable(feature = "stdin_forwarders", since = "1.62.0")] -// pub fn lines(self) -> Lines> { -// self.lock().lines() -// } -// } - -// #[stable(feature = "std_debug", since = "1.16.0")] -// impl fmt::Debug for Stdin { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// f.debug_struct("Stdin").finish_non_exhaustive() -// } -// } - -// #[stable(feature = "rust1", since = "1.0.0")] -// impl Read for Stdin { -// fn read(&mut self, buf: &mut [u8]) -> io::Result { -// self.lock().read(buf) -// } -// fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { -// self.lock().read_buf(buf) -// } -// fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { -// self.lock().read_vectored(bufs) -// } -// #[inline] -// fn is_read_vectored(&self) -> bool { -// self.lock().is_read_vectored() -// } -// fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { -// self.lock().read_to_end(buf) -// } -// fn read_to_string(&mut self, buf: &mut String) -> io::Result { -// self.lock().read_to_string(buf) -// } -// fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { -// self.lock().read_exact(buf) -// } -// fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { -// self.lock().read_buf_exact(cursor) -// } -// } - -// #[stable(feature = "read_shared_stdin", since = "1.78.0")] -// impl Read for &Stdin { -// fn read(&mut self, buf: &mut [u8]) -> io::Result { -// self.lock().read(buf) -// } -// fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { -// self.lock().read_buf(buf) -// } -// fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { -// self.lock().read_vectored(bufs) -// } -// #[inline] -// fn is_read_vectored(&self) -> bool { -// self.lock().is_read_vectored() -// } -// fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { -// self.lock().read_to_end(buf) -// } -// fn read_to_string(&mut self, buf: &mut String) -> io::Result { -// self.lock().read_to_string(buf) -// } -// fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { -// self.lock().read_exact(buf) -// } -// fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { -// self.lock().read_buf_exact(cursor) -// } -// } - -// // only used by platform-dependent io::copy specializations, i.e. unused on some platforms -// #[cfg(any(target_os = "linux", target_os = "android"))] -// impl StdinLock<'_> { -// pub(crate) fn as_mut_buf(&mut self) -> &mut BufReader { -// &mut self.inner -// } -// } - -// #[stable(feature = "rust1", since = "1.0.0")] -// impl Read for StdinLock<'_> { -// fn read(&mut self, buf: &mut [u8]) -> io::Result { -// self.inner.read(buf) -// } - -// fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { -// self.inner.read_buf(buf) -// } - -// fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { -// self.inner.read_vectored(bufs) -// } - -// #[inline] -// fn is_read_vectored(&self) -> bool { -// self.inner.is_read_vectored() -// } - -// fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { -// self.inner.read_to_end(buf) -// } - -// fn read_to_string(&mut self, buf: &mut String) -> io::Result { -// self.inner.read_to_string(buf) -// } - -// fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { -// self.inner.read_exact(buf) -// } - -// fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { -// self.inner.read_buf_exact(cursor) -// } -// } - -// impl SpecReadByte for StdinLock<'_> { -// #[inline] -// fn spec_read_byte(&mut self) -> Option> { -// BufReader::spec_read_byte(&mut *self.inner) -// } -// } - -// #[stable(feature = "rust1", since = "1.0.0")] -// impl BufRead for StdinLock<'_> { -// fn fill_buf(&mut self) -> io::Result<&[u8]> { -// self.inner.fill_buf() -// } - -// fn consume(&mut self, n: usize) { -// self.inner.consume(n) -// } - -// fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { -// self.inner.read_until(byte, buf) -// } - -// fn read_line(&mut self, buf: &mut String) -> io::Result { -// self.inner.read_line(buf) -// } -// } - -// #[stable(feature = "std_debug", since = "1.16.0")] -// impl fmt::Debug for StdinLock<'_> { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// f.debug_struct("StdinLock").finish_non_exhaustive() -// } -// } - -// /// A handle to the global standard output stream of the current process. -// /// -// /// Each handle shares a global buffer of data to be written to the standard -// /// output stream. Access is also synchronized via a lock and explicit control -// /// over locking is available via the [`lock`] method. -// /// -// /// By default, the handle is line-buffered when connected to a terminal, meaning -// /// it flushes automatically when a newline (`\n`) is encountered. For immediate -// /// output, you can manually call the [`flush`] method. When the handle goes out -// /// of scope, the buffer is automatically flushed. -// /// -// /// Created by the [`io::stdout`] method. -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// /// -// /// [`lock`]: Stdout::lock -// /// [`flush`]: Write::flush -// /// [`io::stdout`]: stdout -// #[stable(feature = "rust1", since = "1.0.0")] -// pub struct Stdout { -// // FIXME: this should be LineWriter or BufWriter depending on the state of -// // stdout (tty or not). Note that if this is not line buffered it -// // should also flush-on-panic or some form of flush-on-abort. -// inner: &'static ReentrantLock>>, -// } - -// /// A locked reference to the [`Stdout`] handle. -// /// -// /// This handle implements the [`Write`] trait, and is constructed via -// /// the [`Stdout::lock`] method. See its documentation for more. -// /// -// /// By default, the handle is line-buffered when connected to a terminal, meaning -// /// it flushes automatically when a newline (`\n`) is encountered. For immediate -// /// output, you can manually call the [`flush`] method. When the handle goes out -// /// of scope, the buffer is automatically flushed. -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// /// -// /// [`flush`]: Write::flush -// #[must_use = "if unused stdout will immediately unlock"] -// #[stable(feature = "rust1", since = "1.0.0")] -// pub struct StdoutLock<'a> { -// inner: ReentrantLockGuard<'a, RefCell>>, -// } - -// static STDOUT: OnceLock>>> = OnceLock::new(); - -// /// Constructs a new handle to the standard output of the current process. -// /// -// /// Each handle returned is a reference to a shared global buffer whose access -// /// is synchronized via a mutex. If you need more explicit control over -// /// locking, see the [`Stdout::lock`] method. -// /// -// /// By default, the handle is line-buffered when connected to a terminal, meaning -// /// it flushes automatically when a newline (`\n`) is encountered. For immediate -// /// output, you can manually call the [`flush`] method. When the handle goes out -// /// of scope, the buffer is automatically flushed. -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// /// -// /// # Examples -// /// -// /// Using implicit synchronization: -// /// -// /// ```no_run -// /// use std::io::{self, Write}; -// /// -// /// fn main() -> io::Result<()> { -// /// io::stdout().write_all(b"hello world")?; -// /// -// /// Ok(()) -// /// } -// /// ``` -// /// -// /// Using explicit synchronization: -// /// -// /// ```no_run -// /// use std::io::{self, Write}; -// /// -// /// fn main() -> io::Result<()> { -// /// let stdout = io::stdout(); -// /// let mut handle = stdout.lock(); -// /// -// /// handle.write_all(b"hello world")?; -// /// -// /// Ok(()) -// /// } -// /// ``` -// /// -// /// Ensuring output is flushed immediately: -// /// -// /// ```no_run -// /// use std::io::{self, Write}; -// /// -// /// fn main() -> io::Result<()> { -// /// let mut stdout = io::stdout(); -// /// stdout.write_all(b"hello, ")?; -// /// stdout.flush()?; // Manual flush -// /// stdout.write_all(b"world!\n")?; // Automatically flushed -// /// Ok(()) -// /// } -// /// ``` -// /// -// /// [`flush`]: Write::flush -// #[must_use] -// #[stable(feature = "rust1", since = "1.0.0")] -// #[cfg_attr(not(test), rustc_diagnostic_item = "io_stdout")] -// pub fn stdout() -> Stdout { -// Stdout { -// inner: STDOUT -// .get_or_init(|| ReentrantLock::new(RefCell::new(LineWriter::new(stdout_raw())))), -// } -// } - -// // Flush the data and disable buffering during shutdown -// // by replacing the line writer by one with zero -// // buffering capacity. -// pub fn cleanup() { -// let mut initialized = false; -// let stdout = STDOUT.get_or_init(|| { -// initialized = true; -// ReentrantLock::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw()))) -// }); - -// if !initialized { -// // The buffer was previously initialized, overwrite it here. -// // We use try_lock() instead of lock(), because someone -// // might have leaked a StdoutLock, which would -// // otherwise cause a deadlock here. -// if let Some(lock) = stdout.try_lock() { -// *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); -// } -// } -// } - -// impl Stdout { -// /// Locks this handle to the standard output stream, returning a writable -// /// guard. -// /// -// /// The lock is released when the returned lock goes out of scope. The -// /// returned guard also implements the `Write` trait for writing data. -// /// -// /// # Examples -// /// -// /// ```no_run -// /// use std::io::{self, Write}; -// /// -// /// fn main() -> io::Result<()> { -// /// let mut stdout = io::stdout().lock(); -// /// -// /// stdout.write_all(b"hello world")?; -// /// -// /// Ok(()) -// /// } -// /// ``` -// #[stable(feature = "rust1", since = "1.0.0")] -// pub fn lock(&self) -> StdoutLock<'static> { -// // Locks this handle with 'static lifetime. This depends on the -// // implementation detail that the underlying `ReentrantMutex` is -// // static. -// StdoutLock { inner: self.inner.lock() } -// } -// } - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl UnwindSafe for Stdout {} - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl RefUnwindSafe for Stdout {} - -// #[stable(feature = "std_debug", since = "1.16.0")] -// impl fmt::Debug for Stdout { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// f.debug_struct("Stdout").finish_non_exhaustive() -// } -// } - -// #[stable(feature = "rust1", since = "1.0.0")] -// impl Write for Stdout { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// (&*self).write(buf) -// } -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// (&*self).write_vectored(bufs) -// } -// #[inline] -// fn is_write_vectored(&self) -> bool { -// io::Write::is_write_vectored(&&*self) -// } -// fn flush(&mut self) -> io::Result<()> { -// (&*self).flush() -// } -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// (&*self).write_all(buf) -// } -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// (&*self).write_all_vectored(bufs) -// } -// fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { -// (&*self).write_fmt(args) -// } -// } - -// #[stable(feature = "write_mt", since = "1.48.0")] -// impl Write for &Stdout { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// self.lock().write(buf) -// } -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// self.lock().write_vectored(bufs) -// } -// #[inline] -// fn is_write_vectored(&self) -> bool { -// self.lock().is_write_vectored() -// } -// fn flush(&mut self) -> io::Result<()> { -// self.lock().flush() -// } -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// self.lock().write_all(buf) -// } -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// self.lock().write_all_vectored(bufs) -// } -// fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { -// self.lock().write_fmt(args) -// } -// } - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl UnwindSafe for StdoutLock<'_> {} - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl RefUnwindSafe for StdoutLock<'_> {} - -// #[stable(feature = "rust1", since = "1.0.0")] -// impl Write for StdoutLock<'_> { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// self.inner.borrow_mut().write(buf) -// } -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// self.inner.borrow_mut().write_vectored(bufs) -// } -// #[inline] -// fn is_write_vectored(&self) -> bool { -// self.inner.borrow_mut().is_write_vectored() -// } -// fn flush(&mut self) -> io::Result<()> { -// self.inner.borrow_mut().flush() -// } -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// self.inner.borrow_mut().write_all(buf) -// } -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// self.inner.borrow_mut().write_all_vectored(bufs) -// } -// } - -// #[stable(feature = "std_debug", since = "1.16.0")] -// impl fmt::Debug for StdoutLock<'_> { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// f.debug_struct("StdoutLock").finish_non_exhaustive() -// } -// } - -// /// A handle to the standard error stream of a process. -// /// -// /// For more information, see the [`io::stderr`] method. -// /// -// /// [`io::stderr`]: stderr -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// #[stable(feature = "rust1", since = "1.0.0")] -// pub struct Stderr { -// inner: &'static ReentrantLock>, -// } - -// /// A locked reference to the [`Stderr`] handle. -// /// -// /// This handle implements the [`Write`] trait and is constructed via -// /// the [`Stderr::lock`] method. See its documentation for more. -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// #[must_use = "if unused stderr will immediately unlock"] -// #[stable(feature = "rust1", since = "1.0.0")] -// pub struct StderrLock<'a> { -// inner: ReentrantLockGuard<'a, RefCell>, -// } - -// /// Constructs a new handle to the standard error of the current process. -// /// -// /// This handle is not buffered. -// /// -// /// ### Note: Windows Portability Considerations -// /// -// /// When operating in a console, the Windows implementation of this stream does not support -// /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return -// /// an error. -// /// -// /// In a process with a detached console, such as one using -// /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, -// /// the contained handle will be null. In such cases, the standard library's `Read` and -// /// `Write` will do nothing and silently succeed. All other I/O operations, via the -// /// standard library or via raw Windows API calls, will fail. -// /// -// /// # Examples -// /// -// /// Using implicit synchronization: -// /// -// /// ```no_run -// /// use std::io::{self, Write}; -// /// -// /// fn main() -> io::Result<()> { -// /// io::stderr().write_all(b"hello world")?; -// /// -// /// Ok(()) -// /// } -// /// ``` -// /// -// /// Using explicit synchronization: -// /// -// /// ```no_run -// /// use std::io::{self, Write}; -// /// -// /// fn main() -> io::Result<()> { -// /// let stderr = io::stderr(); -// /// let mut handle = stderr.lock(); -// /// -// /// handle.write_all(b"hello world")?; -// /// -// /// Ok(()) -// /// } -// /// ``` -// #[must_use] -// #[stable(feature = "rust1", since = "1.0.0")] -// #[cfg_attr(not(test), rustc_diagnostic_item = "io_stderr")] -// pub fn stderr() -> Stderr { -// // Note that unlike `stdout()` we don't use `at_exit` here to register a -// // destructor. Stderr is not buffered, so there's no need to run a -// // destructor for flushing the buffer -// static INSTANCE: ReentrantLock> = -// ReentrantLock::new(RefCell::new(stderr_raw())); - -// Stderr { inner: &INSTANCE } -// } - -// impl Stderr { -// /// Locks this handle to the standard error stream, returning a writable -// /// guard. -// /// -// /// The lock is released when the returned lock goes out of scope. The -// /// returned guard also implements the [`Write`] trait for writing data. -// /// -// /// # Examples -// /// -// /// ``` -// /// use std::io::{self, Write}; -// /// -// /// fn foo() -> io::Result<()> { -// /// let stderr = io::stderr(); -// /// let mut handle = stderr.lock(); -// /// -// /// handle.write_all(b"hello world")?; -// /// -// /// Ok(()) -// /// } -// /// ``` -// #[stable(feature = "rust1", since = "1.0.0")] -// pub fn lock(&self) -> StderrLock<'static> { -// // Locks this handle with 'static lifetime. This depends on the -// // implementation detail that the underlying `ReentrantMutex` is -// // static. -// StderrLock { inner: self.inner.lock() } -// } -// } - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl UnwindSafe for Stderr {} - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl RefUnwindSafe for Stderr {} - -// #[stable(feature = "std_debug", since = "1.16.0")] -// impl fmt::Debug for Stderr { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// f.debug_struct("Stderr").finish_non_exhaustive() -// } -// } - -// #[stable(feature = "rust1", since = "1.0.0")] -// impl Write for Stderr { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// (&*self).write(buf) -// } -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// (&*self).write_vectored(bufs) -// } -// #[inline] -// fn is_write_vectored(&self) -> bool { -// io::Write::is_write_vectored(&&*self) -// } -// fn flush(&mut self) -> io::Result<()> { -// (&*self).flush() -// } -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// (&*self).write_all(buf) -// } -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// (&*self).write_all_vectored(bufs) -// } -// fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { -// (&*self).write_fmt(args) -// } -// } - -// #[stable(feature = "write_mt", since = "1.48.0")] -// impl Write for &Stderr { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// self.lock().write(buf) -// } -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// self.lock().write_vectored(bufs) -// } -// #[inline] -// fn is_write_vectored(&self) -> bool { -// self.lock().is_write_vectored() -// } -// fn flush(&mut self) -> io::Result<()> { -// self.lock().flush() -// } -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// self.lock().write_all(buf) -// } -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// self.lock().write_all_vectored(bufs) -// } -// fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { -// self.lock().write_fmt(args) -// } -// } - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl UnwindSafe for StderrLock<'_> {} - -// #[stable(feature = "catch_unwind", since = "1.9.0")] -// impl RefUnwindSafe for StderrLock<'_> {} - -// #[stable(feature = "rust1", since = "1.0.0")] -// impl Write for StderrLock<'_> { -// fn write(&mut self, buf: &[u8]) -> io::Result { -// self.inner.borrow_mut().write(buf) -// } -// fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { -// self.inner.borrow_mut().write_vectored(bufs) -// } -// #[inline] -// fn is_write_vectored(&self) -> bool { -// self.inner.borrow_mut().is_write_vectored() -// } -// fn flush(&mut self) -> io::Result<()> { -// self.inner.borrow_mut().flush() -// } -// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { -// self.inner.borrow_mut().write_all(buf) -// } -// fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { -// self.inner.borrow_mut().write_all_vectored(bufs) -// } -// } - -// #[stable(feature = "std_debug", since = "1.16.0")] -// impl fmt::Debug for StderrLock<'_> { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// f.debug_struct("StderrLock").finish_non_exhaustive() -// } -// } - -// /// Sets the thread-local output capture buffer and returns the old one. -// #[unstable( -// feature = "internal_output_capture", -// reason = "this function is meant for use in the test crate \ -// and may disappear in the future", -// issue = "none" -// )] -// #[doc(hidden)] -// pub fn set_output_capture(sink: Option) -> Option { -// try_set_output_capture(sink).expect( -// "cannot access a Thread Local Storage value \ -// during or after destruction", -// ) -// } +thread_local! { + /// Used by the test crate to capture the output of the print macros and panics. + static OUTPUT_CAPTURE: Cell> = const { + Cell::new(None) + } +} + +/// Flag to indicate OUTPUT_CAPTURE is used. +/// +/// If it is None and was never set on any thread, this flag is set to false, +/// and OUTPUT_CAPTURE can be safely ignored on all threads, saving some time +/// and memory registering an unused thread local. +/// +/// Note about memory ordering: This contains information about whether a +/// thread local variable might be in use. Although this is a global flag, the +/// memory ordering between threads does not matter: we only want this flag to +/// have a consistent order between set_output_capture and print_to *within +/// the same thread*. Within the same thread, things always have a perfectly +/// consistent order. So Ordering::Relaxed is fine. +static OUTPUT_CAPTURE_USED: Atomic = AtomicBool::new(false); + +/// A handle to a raw instance of the standard input stream of this process. +/// +/// This handle is not synchronized or buffered in any fashion. Constructed via +/// the `std::io::stdio::stdin_raw` function. +struct StdinRaw(stdio::Stdin); + +/// A handle to a raw instance of the standard output stream of this process. +/// +/// This handle is not synchronized or buffered in any fashion. Constructed via +/// the `std::io::stdio::stdout_raw` function. +struct StdoutRaw(stdio::Stdout); + +/// A handle to a raw instance of the standard output stream of this process. +/// +/// This handle is not synchronized or buffered in any fashion. Constructed via +/// the `std::io::stdio::stderr_raw` function. +struct StderrRaw(stdio::Stderr); + +/// Constructs a new raw handle to the standard input of this process. +/// +/// The returned handle does not interact with any other handles created nor +/// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin` +/// handles is **not** available to raw handles returned from this function. +/// +/// The returned handle has no external synchronization or buffering. +#[unstable(feature = "libstd_sys_internals", issue = "none")] +const fn stdin_raw() -> StdinRaw { + StdinRaw(stdio::Stdin::new()) +} + +/// Constructs a new raw handle to the standard output stream of this process. +/// +/// The returned handle does not interact with any other handles created nor +/// handles returned by `std::io::stdout`. Note that data is buffered by the +/// `std::io::stdout` handles so writes which happen via this raw handle may +/// appear before previous writes. +/// +/// The returned handle has no external synchronization or buffering layered on +/// top. +#[unstable(feature = "libstd_sys_internals", issue = "none")] +const fn stdout_raw() -> StdoutRaw { + StdoutRaw(stdio::Stdout::new()) +} + +/// Constructs a new raw handle to the standard error stream of this process. +/// +/// The returned handle does not interact with any other handles created nor +/// handles returned by `std::io::stderr`. +/// +/// The returned handle has no external synchronization or buffering layered on +/// top. +#[unstable(feature = "libstd_sys_internals", issue = "none")] +const fn stderr_raw() -> StderrRaw { + StderrRaw(stdio::Stderr::new()) +} + +impl Read for StdinRaw { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + handle_ebadf(self.0.read(buf), || Ok(0)) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + handle_ebadf(self.0.read_buf(buf), || Ok(())) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + handle_ebadf(self.0.read_vectored(bufs), || Ok(0)) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if buf.is_empty() { + return Ok(()); + } + handle_ebadf(self.0.read_exact(buf), || Err(io::Error::READ_EXACT_EOF)) + } + + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + if buf.capacity() == 0 { + return Ok(()); + } + handle_ebadf(self.0.read_buf_exact(buf), || Err(io::Error::READ_EXACT_EOF)) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + handle_ebadf(self.0.read_to_end(buf), || Ok(0)) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + handle_ebadf(self.0.read_to_string(buf), || Ok(0)) + } +} + +impl Write for StdoutRaw { + fn write(&mut self, buf: &[u8]) -> io::Result { + handle_ebadf(self.0.write(buf), || Ok(buf.len())) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total = || Ok(bufs.iter().map(|b| b.len()).sum()); + handle_ebadf(self.0.write_vectored(bufs), total) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + handle_ebadf(self.0.flush(), || Ok(())) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + handle_ebadf(self.0.write_all(buf), || Ok(())) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + handle_ebadf(self.0.write_fmt(fmt), || Ok(())) + } +} + +impl Write for StderrRaw { + fn write(&mut self, buf: &[u8]) -> io::Result { + handle_ebadf(self.0.write(buf), || Ok(buf.len())) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total = || Ok(bufs.iter().map(|b| b.len()).sum()); + handle_ebadf(self.0.write_vectored(bufs), total) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + handle_ebadf(self.0.flush(), || Ok(())) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + handle_ebadf(self.0.write_all(buf), || Ok(())) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + handle_ebadf(self.0.write_all_vectored(bufs), || Ok(())) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + handle_ebadf(self.0.write_fmt(fmt), || Ok(())) + } +} + +fn handle_ebadf(r: io::Result, default: impl FnOnce() -> io::Result) -> io::Result { + match r { + Err(ref e) if stdio::is_ebadf(e) => default(), + r => r, + } +} + +/// A handle to the standard input stream of a process. +/// +/// Each handle is a shared reference to a global buffer of input data to this +/// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods +/// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect +/// to other reads. +/// +/// This handle implements the `Read` trait, but beware that concurrent reads +/// of `Stdin` must be executed with care. +/// +/// Created by the [`io::stdin`] method. +/// +/// [`io::stdin`]: stdin +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// +/// # Examples +/// +/// ```no_run +/// use std::io; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// let stdin = io::stdin(); // We get `Stdin` here. +/// stdin.read_line(&mut buffer)?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Stdin")] +pub struct Stdin { + inner: &'static Mutex>, +} + +/// A locked reference to the [`Stdin`] handle. +/// +/// This handle implements both the [`Read`] and [`BufRead`] traits, and +/// is constructed via the [`Stdin::lock`] method. +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{self, BufRead}; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// let stdin = io::stdin(); // We get `Stdin` here. +/// { +/// let mut handle = stdin.lock(); // We get `StdinLock` here. +/// handle.read_line(&mut buffer)?; +/// } // `StdinLock` is dropped here. +/// Ok(()) +/// } +/// ``` +#[must_use = "if unused stdin will immediately unlock"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct StdinLock<'a> { + inner: MutexGuard<'a, BufReader>, +} + +/// Constructs a new handle to the standard input of the current process. +/// +/// Each handle returned is a reference to a shared global buffer whose access +/// is synchronized via a mutex. If you need more explicit control over +/// locking, see the [`Stdin::lock`] method. +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// +/// # Examples +/// +/// Using implicit synchronization: +/// +/// ```no_run +/// use std::io; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// io::stdin().read_line(&mut buffer)?; +/// Ok(()) +/// } +/// ``` +/// +/// Using explicit synchronization: +/// +/// ```no_run +/// use std::io::{self, BufRead}; +/// +/// fn main() -> io::Result<()> { +/// let mut buffer = String::new(); +/// let stdin = io::stdin(); +/// let mut handle = stdin.lock(); +/// +/// handle.read_line(&mut buffer)?; +/// Ok(()) +/// } +/// ``` +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn stdin() -> Stdin { + static INSTANCE: OnceLock>> = OnceLock::new(); + Stdin { + inner: INSTANCE.get_or_init(|| { + Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin_raw())) + }), + } +} + +impl Stdin { + /// Locks this handle to the standard input stream, returning a readable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the [`Read`] and [`BufRead`] traits for + /// accessing the underlying data. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{self, BufRead}; + /// + /// fn main() -> io::Result<()> { + /// let mut buffer = String::new(); + /// let stdin = io::stdin(); + /// let mut handle = stdin.lock(); + /// + /// handle.read_line(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn lock(&self) -> StdinLock<'static> { + // Locks this handle with 'static lifetime. This depends on the + // implementation detail that the underlying `Mutex` is static. + StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + } + + /// Locks this handle and reads a line of input, appending it to the specified buffer. + /// + /// For detailed semantics of this method, see the documentation on + /// [`BufRead::read_line`]. In particular: + /// * Previous content of the buffer will be preserved. To avoid appending + /// to the buffer, you need to [`clear`] it first. + /// * The trailing newline character, if any, is included in the buffer. + /// + /// [`clear`]: String::clear + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// + /// let mut input = String::new(); + /// match io::stdin().read_line(&mut input) { + /// Ok(n) => { + /// println!("{n} bytes read"); + /// println!("{input}"); + /// } + /// Err(error) => println!("error: {error}"), + /// } + /// ``` + /// + /// You can run the example one of two ways: + /// + /// - Pipe some text to it, e.g., `printf foo | path/to/executable` + /// - Give it text interactively by running the executable directly, + /// in which case it will wait for the Enter key to be pressed before + /// continuing + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("get_line")] + pub fn read_line(&self, buf: &mut String) -> io::Result { + self.lock().read_line(buf) + } + + /// Consumes this handle and returns an iterator over input lines. + /// + /// For detailed semantics of this method, see the documentation on + /// [`BufRead::lines`]. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// + /// let lines = io::stdin().lines(); + /// for line in lines { + /// println!("got a line: {}", line.unwrap()); + /// } + /// ``` + #[must_use = "`self` will be dropped if the result is not used"] + #[stable(feature = "stdin_forwarders", since = "1.62.0")] + pub fn lines(self) -> Lines> { + self.lock().lines() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stdin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Stdin").finish_non_exhaustive() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.lock().read(buf) + } + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.lock().read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.lock().is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.lock().read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.lock().read_to_string(buf) + } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.lock().read_exact(buf) + } + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf_exact(cursor) + } +} + +#[stable(feature = "read_shared_stdin", since = "1.78.0")] +impl Read for &Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.lock().read(buf) + } + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.lock().read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.lock().is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.lock().read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.lock().read_to_string(buf) + } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.lock().read_exact(buf) + } + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf_exact(cursor) + } +} + +// only used by platform-dependent io::copy specializations, i.e. unused on some platforms +#[cfg(any(target_os = "linux", target_os = "android"))] +impl StdinLock<'_> { + pub(crate) fn as_mut_buf(&mut self) -> &mut BufReader { + &mut self.inner + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for StdinLock<'_> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.inner.read_to_end(buf) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.inner.read_to_string(buf) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.inner.read_exact(buf) + } + + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf_exact(cursor) + } +} + +impl SpecReadByte for StdinLock<'_> { + #[inline] + fn spec_read_byte(&mut self) -> Option> { + BufReader::spec_read_byte(&mut *self.inner) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for StdinLock<'_> { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + self.inner.fill_buf() + } + + fn consume(&mut self, n: usize) { + self.inner.consume(n) + } + + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + self.inner.read_until(byte, buf) + } + + fn read_line(&mut self, buf: &mut String) -> io::Result { + self.inner.read_line(buf) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for StdinLock<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StdinLock").finish_non_exhaustive() + } +} + +/// A handle to the global standard output stream of the current process. +/// +/// Each handle shares a global buffer of data to be written to the standard +/// output stream. Access is also synchronized via a lock and explicit control +/// over locking is available via the [`lock`] method. +/// +/// By default, the handle is line-buffered when connected to a terminal, meaning +/// it flushes automatically when a newline (`\n`) is encountered. For immediate +/// output, you can manually call the [`flush`] method. When the handle goes out +/// of scope, the buffer is automatically flushed. +/// +/// Created by the [`io::stdout`] method. +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// +/// [`lock`]: Stdout::lock +/// [`flush`]: Write::flush +/// [`io::stdout`]: stdout +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Stdout { + // FIXME: this should be LineWriter or BufWriter depending on the state of + // stdout (tty or not). Note that if this is not line buffered it + // should also flush-on-panic or some form of flush-on-abort. + inner: &'static ReentrantLock>>, +} + +/// A locked reference to the [`Stdout`] handle. +/// +/// This handle implements the [`Write`] trait, and is constructed via +/// the [`Stdout::lock`] method. See its documentation for more. +/// +/// By default, the handle is line-buffered when connected to a terminal, meaning +/// it flushes automatically when a newline (`\n`) is encountered. For immediate +/// output, you can manually call the [`flush`] method. When the handle goes out +/// of scope, the buffer is automatically flushed. +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// +/// [`flush`]: Write::flush +#[must_use = "if unused stdout will immediately unlock"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct StdoutLock<'a> { + inner: ReentrantLockGuard<'a, RefCell>>, +} + +static STDOUT: OnceLock>>> = OnceLock::new(); + +/// Constructs a new handle to the standard output of the current process. +/// +/// Each handle returned is a reference to a shared global buffer whose access +/// is synchronized via a mutex. If you need more explicit control over +/// locking, see the [`Stdout::lock`] method. +/// +/// By default, the handle is line-buffered when connected to a terminal, meaning +/// it flushes automatically when a newline (`\n`) is encountered. For immediate +/// output, you can manually call the [`flush`] method. When the handle goes out +/// of scope, the buffer is automatically flushed. +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// +/// # Examples +/// +/// Using implicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// io::stdout().write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// Using explicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let stdout = io::stdout(); +/// let mut handle = stdout.lock(); +/// +/// handle.write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// Ensuring output is flushed immediately: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let mut stdout = io::stdout(); +/// stdout.write_all(b"hello, ")?; +/// stdout.flush()?; // Manual flush +/// stdout.write_all(b"world!\n")?; // Automatically flushed +/// Ok(()) +/// } +/// ``` +/// +/// [`flush`]: Write::flush +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "io_stdout")] +pub fn stdout() -> Stdout { + Stdout { + inner: STDOUT + .get_or_init(|| ReentrantLock::new(RefCell::new(LineWriter::new(stdout_raw())))), + } +} + +// Flush the data and disable buffering during shutdown +// by replacing the line writer by one with zero +// buffering capacity. +pub fn cleanup() { + let mut initialized = false; + let stdout = STDOUT.get_or_init(|| { + initialized = true; + ReentrantLock::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw()))) + }); + + if !initialized { + // The buffer was previously initialized, overwrite it here. + // We use try_lock() instead of lock(), because someone + // might have leaked a StdoutLock, which would + // otherwise cause a deadlock here. + if let Some(lock) = stdout.try_lock() { + *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); + } + } +} + +impl Stdout { + /// Locks this handle to the standard output stream, returning a writable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the `Write` trait for writing data. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{self, Write}; + /// + /// fn main() -> io::Result<()> { + /// let mut stdout = io::stdout().lock(); + /// + /// stdout.write_all(b"hello world")?; + /// + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn lock(&self) -> StdoutLock<'static> { + // Locks this handle with 'static lifetime. This depends on the + // implementation detail that the underlying `ReentrantMutex` is + // static. + StdoutLock { inner: self.inner.lock() } + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Stdout {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for Stdout {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stdout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Stdout").finish_non_exhaustive() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&*self).write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&*self).write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + io::Write::is_write_vectored(&&*self) + } + fn flush(&mut self) -> io::Result<()> { + (&*self).flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (&*self).write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (&*self).write_all_vectored(bufs) + } + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { + (&*self).write_fmt(args) + } +} + +#[stable(feature = "write_mt", since = "1.48.0")] +impl Write for &Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.lock().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.lock().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.lock().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.lock().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.lock().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.lock().write_all_vectored(bufs) + } + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { + self.lock().write_fmt(args) + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for StdoutLock<'_> {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for StdoutLock<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for StdoutLock<'_> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.borrow_mut().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.borrow_mut().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.borrow_mut().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.inner.borrow_mut().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.inner.borrow_mut().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.inner.borrow_mut().write_all_vectored(bufs) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for StdoutLock<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StdoutLock").finish_non_exhaustive() + } +} + +/// A handle to the standard error stream of a process. +/// +/// For more information, see the [`io::stderr`] method. +/// +/// [`io::stderr`]: stderr +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Stderr { + inner: &'static ReentrantLock>, +} + +/// A locked reference to the [`Stderr`] handle. +/// +/// This handle implements the [`Write`] trait and is constructed via +/// the [`Stderr::lock`] method. See its documentation for more. +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +#[must_use = "if unused stderr will immediately unlock"] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct StderrLock<'a> { + inner: ReentrantLockGuard<'a, RefCell>, +} + +/// Constructs a new handle to the standard error of the current process. +/// +/// This handle is not buffered. +/// +/// ### Note: Windows Portability Considerations +/// +/// When operating in a console, the Windows implementation of this stream does not support +/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return +/// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// +/// # Examples +/// +/// Using implicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// io::stderr().write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +/// +/// Using explicit synchronization: +/// +/// ```no_run +/// use std::io::{self, Write}; +/// +/// fn main() -> io::Result<()> { +/// let stderr = io::stderr(); +/// let mut handle = stderr.lock(); +/// +/// handle.write_all(b"hello world")?; +/// +/// Ok(()) +/// } +/// ``` +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "io_stderr")] +pub fn stderr() -> Stderr { + // Note that unlike `stdout()` we don't use `at_exit` here to register a + // destructor. Stderr is not buffered, so there's no need to run a + // destructor for flushing the buffer + static INSTANCE: ReentrantLock> = + ReentrantLock::new(RefCell::new(stderr_raw())); + + Stderr { inner: &INSTANCE } +} + +impl Stderr { + /// Locks this handle to the standard error stream, returning a writable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the [`Write`] trait for writing data. + /// + /// # Examples + /// + /// ``` + /// use std::io::{self, Write}; + /// + /// fn foo() -> io::Result<()> { + /// let stderr = io::stderr(); + /// let mut handle = stderr.lock(); + /// + /// handle.write_all(b"hello world")?; + /// + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn lock(&self) -> StderrLock<'static> { + // Locks this handle with 'static lifetime. This depends on the + // implementation detail that the underlying `ReentrantMutex` is + // static. + StderrLock { inner: self.inner.lock() } + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for Stderr {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for Stderr {} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stderr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Stderr").finish_non_exhaustive() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&*self).write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&*self).write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + io::Write::is_write_vectored(&&*self) + } + fn flush(&mut self) -> io::Result<()> { + (&*self).flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (&*self).write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (&*self).write_all_vectored(bufs) + } + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { + (&*self).write_fmt(args) + } +} + +#[stable(feature = "write_mt", since = "1.48.0")] +impl Write for &Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.lock().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.lock().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.lock().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.lock().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.lock().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.lock().write_all_vectored(bufs) + } + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { + self.lock().write_fmt(args) + } +} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl UnwindSafe for StderrLock<'_> {} + +#[stable(feature = "catch_unwind", since = "1.9.0")] +impl RefUnwindSafe for StderrLock<'_> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for StderrLock<'_> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.borrow_mut().write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.borrow_mut().write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.inner.borrow_mut().is_write_vectored() + } + fn flush(&mut self) -> io::Result<()> { + self.inner.borrow_mut().flush() + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.inner.borrow_mut().write_all(buf) + } + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.inner.borrow_mut().write_all_vectored(bufs) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for StderrLock<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StderrLock").finish_non_exhaustive() + } +} + +/// Sets the thread-local output capture buffer and returns the old one. +#[unstable( + feature = "internal_output_capture", + reason = "this function is meant for use in the test crate \ + and may disappear in the future", + issue = "none" +)] +#[doc(hidden)] +pub fn set_output_capture(sink: Option) -> Option { + try_set_output_capture(sink).expect( + "cannot access a Thread Local Storage value \ + during or after destruction", + ) +} /// Tries to set the thread-local output capture buffer and returns the old one. /// This may fail once thread-local destructors are called. It's used in panic @@ -1131,162 +1131,160 @@ type LocalStream = Arc>>; pub fn try_set_output_capture( sink: Option, ) -> Result, AccessError> { - Ok(None) - //todo!() - // if sink.is_none() && !OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) { - // // OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false. - // return Ok(None); - // } - // OUTPUT_CAPTURE_USED.store(true, Ordering::Relaxed); - // OUTPUT_CAPTURE.try_with(move |slot| slot.replace(sink)) + if sink.is_none() && !OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) { + // OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false. + return Ok(None); + } + OUTPUT_CAPTURE_USED.store(true, Ordering::Relaxed); + OUTPUT_CAPTURE.try_with(move |slot| slot.replace(sink)) } -// /// Writes `args` to the capture buffer if enabled and possible, or `global_s` -// /// otherwise. `label` identifies the stream in a panic message. -// /// -// /// This function is used to print error messages, so it takes extra -// /// care to avoid causing a panic when `OUTPUT_CAPTURE` is unusable. -// /// For instance, if the TLS key for output capturing is already destroyed, or -// /// if the local stream is in use by another thread, it will just fall back to -// /// the global stream. -// /// -// /// However, if the actual I/O causes an error, this function does panic. -// /// -// /// Writing to non-blocking stdout/stderr can cause an error, which will lead -// /// this function to panic. -// fn print_to(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str) -// where -// T: Write, -// { -// if print_to_buffer_if_capture_used(args) { -// // Successfully wrote to capture buffer. -// return; -// } +/// Writes `args` to the capture buffer if enabled and possible, or `global_s` +/// otherwise. `label` identifies the stream in a panic message. +/// +/// This function is used to print error messages, so it takes extra +/// care to avoid causing a panic when `OUTPUT_CAPTURE` is unusable. +/// For instance, if the TLS key for output capturing is already destroyed, or +/// if the local stream is in use by another thread, it will just fall back to +/// the global stream. +/// +/// However, if the actual I/O causes an error, this function does panic. +/// +/// Writing to non-blocking stdout/stderr can cause an error, which will lead +/// this function to panic. +fn print_to(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str) +where + T: Write, +{ + if print_to_buffer_if_capture_used(args) { + // Successfully wrote to capture buffer. + return; + } -// if let Err(e) = global_s().write_fmt(args) { -// panic!("failed printing to {label}: {e}"); -// } -// } + if let Err(e) = global_s().write_fmt(args) { + panic!("failed printing to {label}: {e}"); + } +} -// fn print_to_buffer_if_capture_used(args: fmt::Arguments<'_>) -> bool { -// OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) -// && OUTPUT_CAPTURE.try_with(|s| { -// // Note that we completely remove a local sink to write to in case -// // our printing recursively panics/prints, so the recursive -// // panic/print goes to the global sink instead of our local sink. -// s.take().map(|w| { -// let _ = w.lock().unwrap_or_else(|e| e.into_inner()).write_fmt(args); -// s.set(Some(w)); -// }) -// }) == Ok(Some(())) -// } +fn print_to_buffer_if_capture_used(args: fmt::Arguments<'_>) -> bool { + OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) + && OUTPUT_CAPTURE.try_with(|s| { + // Note that we completely remove a local sink to write to in case + // our printing recursively panics/prints, so the recursive + // panic/print goes to the global sink instead of our local sink. + s.take().map(|w| { + let _ = w.lock().unwrap_or_else(|e| e.into_inner()).write_fmt(args); + s.set(Some(w)); + }) + }) == Ok(Some(())) +} -// /// Used by impl Termination for Result to print error after `main` or a test -// /// has returned. Should avoid panicking, although we can't help it if one of -// /// the Display impls inside args decides to. -// pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) { -// if print_to_buffer_if_capture_used(args) { -// return; -// } +/// Used by impl Termination for Result to print error after `main` or a test +/// has returned. Should avoid panicking, although we can't help it if one of +/// the Display impls inside args decides to. +pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) { + if print_to_buffer_if_capture_used(args) { + return; + } -// // Ignore error if the write fails, for example because stderr is already -// // closed. There is not much point panicking at this point. -// let _ = stderr().write_fmt(args); -// } + // Ignore error if the write fails, for example because stderr is already + // closed. There is not much point panicking at this point. + let _ = stderr().write_fmt(args); +} -// /// Trait to determine if a descriptor/handle refers to a terminal/tty. -// #[stable(feature = "is_terminal", since = "1.70.0")] -// pub trait IsTerminal: crate::sealed::Sealed { -// /// Returns `true` if the descriptor/handle refers to a terminal/tty. -// /// -// /// On platforms where Rust does not know how to detect a terminal yet, this will return -// /// `false`. This will also return `false` if an unexpected error occurred, such as from -// /// passing an invalid file descriptor. -// /// -// /// # Platform-specific behavior -// /// -// /// On Windows, in addition to detecting consoles, this currently uses some heuristics to -// /// detect older msys/cygwin/mingw pseudo-terminals based on device name: devices with names -// /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals. -// /// Note that this [may change in the future][changes]. -// /// -// /// # Examples -// /// -// /// An example of a type for which `IsTerminal` is implemented is [`Stdin`]: -// /// -// /// ```no_run -// /// use std::io::{self, IsTerminal, Write}; -// /// -// /// fn main() -> io::Result<()> { -// /// let stdin = io::stdin(); -// /// -// /// // Indicate that the user is prompted for input, if this is a terminal. -// /// if stdin.is_terminal() { -// /// print!("> "); -// /// io::stdout().flush()?; -// /// } -// /// -// /// let mut name = String::new(); -// /// let _ = stdin.read_line(&mut name)?; -// /// -// /// println!("Hello {}", name.trim_end()); -// /// -// /// Ok(()) -// /// } -// /// ``` -// /// -// /// The example can be run in two ways: -// /// -// /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable` -// /// it will print: `Hello foo`. -// /// - If you instead run the example interactively by running `path/to/executable` directly, it will -// /// prompt for input. -// /// -// /// [changes]: io#platform-specific-behavior -// /// [`Stdin`]: crate::io::Stdin -// #[doc(alias = "isatty")] -// #[stable(feature = "is_terminal", since = "1.70.0")] -// fn is_terminal(&self) -> bool; -// } +/// Trait to determine if a descriptor/handle refers to a terminal/tty. +#[stable(feature = "is_terminal", since = "1.70.0")] +pub trait IsTerminal: crate::sealed::Sealed { + /// Returns `true` if the descriptor/handle refers to a terminal/tty. + /// + /// On platforms where Rust does not know how to detect a terminal yet, this will return + /// `false`. This will also return `false` if an unexpected error occurred, such as from + /// passing an invalid file descriptor. + /// + /// # Platform-specific behavior + /// + /// On Windows, in addition to detecting consoles, this currently uses some heuristics to + /// detect older msys/cygwin/mingw pseudo-terminals based on device name: devices with names + /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals. + /// Note that this [may change in the future][changes]. + /// + /// # Examples + /// + /// An example of a type for which `IsTerminal` is implemented is [`Stdin`]: + /// + /// ```no_run + /// use std::io::{self, IsTerminal, Write}; + /// + /// fn main() -> io::Result<()> { + /// let stdin = io::stdin(); + /// + /// // Indicate that the user is prompted for input, if this is a terminal. + /// if stdin.is_terminal() { + /// print!("> "); + /// io::stdout().flush()?; + /// } + /// + /// let mut name = String::new(); + /// let _ = stdin.read_line(&mut name)?; + /// + /// println!("Hello {}", name.trim_end()); + /// + /// Ok(()) + /// } + /// ``` + /// + /// The example can be run in two ways: + /// + /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable` + /// it will print: `Hello foo`. + /// - If you instead run the example interactively by running `path/to/executable` directly, it will + /// prompt for input. + /// + /// [changes]: io#platform-specific-behavior + /// [`Stdin`]: crate::io::Stdin + #[doc(alias = "isatty")] + #[stable(feature = "is_terminal", since = "1.70.0")] + fn is_terminal(&self) -> bool; +} -// macro_rules! impl_is_terminal { -// ($($t:ty),*$(,)?) => {$( -// #[unstable(feature = "sealed", issue = "none")] -// impl crate::sealed::Sealed for $t {} +macro_rules! impl_is_terminal { + ($($t:ty),*$(,)?) => {$( + #[unstable(feature = "sealed", issue = "none")] + impl crate::sealed::Sealed for $t {} -// #[stable(feature = "is_terminal", since = "1.70.0")] -// impl IsTerminal for $t { -// #[inline] -// fn is_terminal(&self) -> bool { -// crate::sys::io::is_terminal(self) -// } -// } -// )*} -// } + #[stable(feature = "is_terminal", since = "1.70.0")] + impl IsTerminal for $t { + #[inline] + fn is_terminal(&self) -> bool { + crate::sys::io::is_terminal(self) + } + } + )*} +} -// impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>); +impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>); -// #[unstable( -// feature = "print_internals", -// reason = "implementation detail which may disappear or be replaced at any time", -// issue = "none" -// )] -// #[doc(hidden)] -// #[cfg(not(test))] -// pub fn _print(args: fmt::Arguments<'_>) { -// print_to(args, stdout, "stdout"); -// } +#[unstable( + feature = "print_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +#[doc(hidden)] +#[cfg(not(test))] +pub fn _print(args: fmt::Arguments<'_>) { + print_to(args, stdout, "stdout"); +} -// #[unstable( -// feature = "print_internals", -// reason = "implementation detail which may disappear or be replaced at any time", -// issue = "none" -// )] -// #[doc(hidden)] -// #[cfg(not(test))] -// pub fn _eprint(args: fmt::Arguments<'_>) { -// print_to(args, stderr, "stderr"); -// } +#[unstable( + feature = "print_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +#[doc(hidden)] +#[cfg(not(test))] +pub fn _eprint(args: fmt::Arguments<'_>) { + print_to(args, stderr, "stderr"); +} -// #[cfg(test)] -// pub use realstd::io::{_eprint, _print}; +#[cfg(test)] +pub use realstd::io::{_eprint, _print}; diff --git a/crates/std/src/lib.rs b/crates/std/src/lib.rs index 3afee45..29e057d 100644 --- a/crates/std/src/lib.rs +++ b/crates/std/src/lib.rs @@ -187,7 +187,8 @@ stable_features, incomplete_features, unexpected_cfgs, - unfulfilled_lint_expectations + unfulfilled_lint_expectations, + private_interfaces )] #![allow(unused)] @@ -287,6 +288,7 @@ pub mod alloc; pub mod bstr; pub mod collections; pub mod env; +pub mod os; pub mod panic; pub mod panicking; pub mod sync; diff --git a/crates/std/src/os/mod.rs b/crates/std/src/os/mod.rs new file mode 100644 index 0000000..7637440 --- /dev/null +++ b/crates/std/src/os/mod.rs @@ -0,0 +1,198 @@ +//! OS-specific functionality. + +#![stable(feature = "os", since = "1.0.0")] +#![allow(missing_docs, nonstandard_style, missing_debug_implementations)] +#![allow(unsafe_op_in_unsafe_fn)] + +pub mod raw; + +// The code below could be written clearer using `cfg_if!`. However, the items below are +// publicly exported by `std` and external tools can have trouble analysing them because of the use +// of a macro that is not vendored by Rust and included in the toolchain. +// See https://github.com/rust-analyzer/rust-analyzer/issues/6038. + +// On certain platforms right now the "main modules" modules that are +// documented don't compile (missing things in `libc` which is empty), +// so just omit them with an empty module and add the "unstable" attribute. + +// darwin, unix, linux, wasi and windows are handled a bit differently. +#[cfg(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +))] +#[unstable(issue = "none", feature = "std_internals")] +pub mod darwin {} +#[cfg(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +))] +#[unstable(issue = "none", feature = "std_internals")] +pub mod unix {} +#[cfg(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +))] +#[unstable(issue = "none", feature = "std_internals")] +pub mod linux {} +#[cfg(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +))] +#[unstable(issue = "none", feature = "std_internals")] +pub mod wasi {} +#[cfg(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +))] +#[unstable(issue = "none", feature = "std_internals")] +pub mod windows {} + +// darwin +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] +#[cfg(any(target_vendor = "apple", doc))] +pub mod darwin; + +// unix +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] +#[cfg(all(not(target_os = "hermit"), any(unix, doc)))] +pub mod unix; + +// linux +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] +#[cfg(any(target_os = "linux", doc))] +pub mod linux; + +// wasi +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] +#[cfg(any(target_os = "wasi", any(target_env = "p1", target_env = "p2"), doc))] +pub mod wasi; + +#[cfg(any(all(target_os = "wasi", target_env = "p2"), doc))] +pub mod wasip2; + +// windows +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] +#[cfg(any(windows, doc))] +pub mod windows; + +// Others. +#[cfg(target_os = "aix")] +pub mod aix; +#[cfg(target_os = "android")] +pub mod android; +#[cfg(target_os = "cygwin")] +pub mod cygwin; +#[cfg(target_os = "dragonfly")] +pub mod dragonfly; +#[cfg(target_os = "emscripten")] +pub mod emscripten; +#[cfg(target_os = "espidf")] +pub mod espidf; +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +pub mod fortanix_sgx; +#[cfg(target_os = "freebsd")] +pub mod freebsd; +#[cfg(target_os = "fuchsia")] +pub mod fuchsia; +#[cfg(target_os = "haiku")] +pub mod haiku; +#[cfg(target_os = "hermit")] +pub mod hermit; +#[cfg(target_os = "horizon")] +pub mod horizon; +#[cfg(target_os = "hurd")] +pub mod hurd; +#[cfg(target_os = "illumos")] +pub mod illumos; +#[cfg(target_os = "ios")] +pub mod ios; +#[cfg(target_os = "l4re")] +pub mod l4re; +#[cfg(target_os = "macos")] +pub mod macos; +#[cfg(target_os = "motor")] +pub mod motor; +#[cfg(target_os = "netbsd")] +pub mod netbsd; +#[cfg(target_os = "nto")] +pub mod nto; +#[cfg(target_os = "nuttx")] +pub mod nuttx; +#[cfg(target_os = "openbsd")] +pub mod openbsd; +#[cfg(target_os = "redox")] +pub mod redox; +#[cfg(target_os = "rtems")] +pub mod rtems; +#[cfg(target_os = "solaris")] +pub mod solaris; +#[cfg(target_os = "solid_asp3")] +pub mod solid; +#[cfg(target_os = "trusty")] +pub mod trusty; +#[cfg(target_os = "uefi")] +pub mod uefi; +#[cfg(target_os = "vita")] +pub mod vita; +#[cfg(target_os = "vxworks")] +pub mod vxworks; +#[cfg(target_os = "xous")] +pub mod xous; + +#[cfg(any( + unix, + target_os = "hermit", + target_os = "trusty", + target_os = "wasi", + target_os = "motor", + doc +))] +pub mod fd; + +#[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin", doc))] +mod net; diff --git a/crates/std/src/os/raw/mod.rs b/crates/std/src/os/raw/mod.rs new file mode 100644 index 0000000..680d553 --- /dev/null +++ b/crates/std/src/os/raw/mod.rs @@ -0,0 +1,26 @@ +//! Compatibility module for C platform-specific types. Use [`core::ffi`] instead. + +#![stable(feature = "raw_os", since = "1.1.0")] + +#[cfg(test)] +mod tests; + +macro_rules! alias_core_ffi { + ($($t:ident)*) => {$( + #[stable(feature = "raw_os", since = "1.1.0")] + // todo retreive docs + #[doc(cfg(all()))] + pub type $t = core::ffi::$t; + )*} +} + +alias_core_ffi! { + c_char c_schar c_uchar + c_short c_ushort + c_int c_uint + c_long c_ulong + c_longlong c_ulonglong + c_float + c_double + c_void +} diff --git a/crates/std/src/os/raw/tests.rs b/crates/std/src/os/raw/tests.rs new file mode 100644 index 0000000..f41a22e --- /dev/null +++ b/crates/std/src/os/raw/tests.rs @@ -0,0 +1,17 @@ +#![cfg(not(all(windows, target_env = "msvc")))] + +use crate::any::TypeId; + +macro_rules! ok { + ($($t:ident)*) => {$( + assert!(TypeId::of::() == TypeId::of::(), + "{} is wrong", stringify!($t)); + )*} +} + +#[test] +fn same() { + use crate::os::raw; + ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong + c_longlong c_ulonglong c_float c_double); +} diff --git a/crates/std/src/prelude.rs b/crates/std/src/prelude.rs index 2771679..79e0617 100644 --- a/crates/std/src/prelude.rs +++ b/crates/std/src/prelude.rs @@ -176,6 +176,6 @@ pub mod rust_2024 { ) -> isize { println!("{}", argc); println!("{:?}", argv); - main().report().to_isize() + main().report().to_i32() as isize } } diff --git a/crates/std/src/process.rs b/crates/std/src/process.rs index d6ab4f1..0d7e5d1 100644 --- a/crates/std/src/process.rs +++ b/crates/std/src/process.rs @@ -1,33 +1,2619 @@ -pub struct ExitCode(isize); +//! A module for working with processes. +//! +//! This module is mostly concerned with spawning and interacting with child +//! processes, but it also provides [`abort`] and [`exit`] for terminating the +//! current process. +//! +//! # Spawning a process +//! +//! The [`Command`] struct is used to configure and spawn processes: +//! +//! ```no_run +//! use std::process::Command; +//! +//! let output = Command::new("echo") +//! .arg("Hello world") +//! .output() +//! .expect("Failed to execute command"); +//! +//! assert_eq!(b"Hello world\n", output.stdout.as_slice()); +//! ``` +//! +//! Several methods on [`Command`], such as [`spawn`] or [`output`], can be used +//! to spawn a process. In particular, [`output`] spawns the child process and +//! waits until the process terminates, while [`spawn`] will return a [`Child`] +//! that represents the spawned child process. +//! +//! # Handling I/O +//! +//! The [`stdout`], [`stdin`], and [`stderr`] of a child process can be +//! configured by passing an [`Stdio`] to the corresponding method on +//! [`Command`]. Once spawned, they can be accessed from the [`Child`]. For +//! example, piping output from one command into another command can be done +//! like so: +//! +//! ```no_run +//! use std::process::{Command, Stdio}; +//! +//! // stdout must be configured with `Stdio::piped` in order to use +//! // `echo_child.stdout` +//! let echo_child = Command::new("echo") +//! .arg("Oh no, a tpyo!") +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("Failed to start echo process"); +//! +//! // Note that `echo_child` is moved here, but we won't be needing +//! // `echo_child` anymore +//! let echo_out = echo_child.stdout.expect("Failed to open echo stdout"); +//! +//! let mut sed_child = Command::new("sed") +//! .arg("s/tpyo/typo/") +//! .stdin(Stdio::from(echo_out)) +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("Failed to start sed process"); +//! +//! let output = sed_child.wait_with_output().expect("Failed to wait on sed"); +//! assert_eq!(b"Oh no, a typo!\n", output.stdout.as_slice()); +//! ``` +//! +//! Note that [`ChildStderr`] and [`ChildStdout`] implement [`Read`] and +//! [`ChildStdin`] implements [`Write`]: +//! +//! ```no_run +//! use std::process::{Command, Stdio}; +//! use std::io::Write; +//! +//! let mut child = Command::new("/bin/cat") +//! .stdin(Stdio::piped()) +//! .stdout(Stdio::piped()) +//! .spawn() +//! .expect("failed to execute child"); +//! +//! // If the child process fills its stdout buffer, it may end up +//! // waiting until the parent reads the stdout, and not be able to +//! // read stdin in the meantime, causing a deadlock. +//! // Writing from another thread ensures that stdout is being read +//! // at the same time, avoiding the problem. +//! let mut stdin = child.stdin.take().expect("failed to get stdin"); +//! std::thread::spawn(move || { +//! stdin.write_all(b"test").expect("failed to write to stdin"); +//! }); +//! +//! let output = child +//! .wait_with_output() +//! .expect("failed to wait on child"); +//! +//! assert_eq!(b"test", output.stdout.as_slice()); +//! ``` +//! +//! # Windows argument splitting +//! +//! On Unix systems arguments are passed to a new process as an array of strings, +//! but on Windows arguments are passed as a single commandline string and it is +//! up to the child process to parse it into an array. Therefore the parent and +//! child processes must agree on how the commandline string is encoded. +//! +//! Most programs use the standard C run-time `argv`, which in practice results +//! in consistent argument handling. However, some programs have their own way of +//! parsing the commandline string. In these cases using [`arg`] or [`args`] may +//! result in the child process seeing a different array of arguments than the +//! parent process intended. +//! +//! Two ways of mitigating this are: +//! +//! * Validate untrusted input so that only a safe subset is allowed. +//! * Use [`raw_arg`] to build a custom commandline. This bypasses the escaping +//! rules used by [`arg`] so should be used with due caution. +//! +//! `cmd.exe` and `.bat` files use non-standard argument parsing and are especially +//! vulnerable to malicious input as they may be used to run arbitrary shell +//! commands. Untrusted arguments should be restricted as much as possible. +//! For examples on handling this see [`raw_arg`]. +//! +//! ### Batch file special handling +//! +//! On Windows, `Command` uses the Windows API function [`CreateProcessW`] to +//! spawn new processes. An undocumented feature of this function is that +//! when given a `.bat` file as the application to run, it will automatically +//! convert that into running `cmd.exe /c` with the batch file as the next argument. +//! +//! For historical reasons Rust currently preserves this behavior when using +//! [`Command::new`], and escapes the arguments according to `cmd.exe` rules. +//! Due to the complexity of `cmd.exe` argument handling, it might not be +//! possible to safely escape some special characters, and using them will result +//! in an error being returned at process spawn. The set of unescapeable +//! special characters might change between releases. +//! +//! Also note that running batch scripts in this way may be removed in the +//! future and so should not be relied upon. +//! +//! [`spawn`]: Command::spawn +//! [`output`]: Command::output +//! +//! [`stdout`]: Command::stdout +//! [`stdin`]: Command::stdin +//! [`stderr`]: Command::stderr +//! +//! [`Write`]: io::Write +//! [`Read`]: io::Read +//! +//! [`arg`]: Command::arg +//! [`args`]: Command::args +//! [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg +//! +//! [`CreateProcessW`]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(0); +#![stable(feature = "process", since = "1.0.0")] +#![deny(unsafe_op_in_unsafe_fn)] - pub fn to_isize(self) -> isize { - self.0 +#[cfg(all( + test, + not(any( + target_os = "emscripten", + target_os = "wasi", + target_env = "sgx", + target_os = "xous", + target_os = "trusty", + )) +))] +mod tests; + +use crate::convert::Infallible; +use crate::ffi::OsStr; +use crate::io::prelude::*; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::num::NonZero; +use crate::path::Path; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, process as imp}; +use crate::{fmt, format_args_nl, fs, str}; + +/// Representation of a running or exited child process. +/// +/// This structure is used to represent and manage child processes. A child +/// process is created via the [`Command`] struct, which configures the +/// spawning process and can itself be constructed using a builder-style +/// interface. +/// +/// There is no implementation of [`Drop`] for child processes, +/// so if you do not ensure the `Child` has exited then it will continue to +/// run, even after the `Child` handle to the child process has gone out of +/// scope. +/// +/// Calling [`wait`] (or other functions that wrap around it) will make +/// the parent process wait until the child has actually exited before +/// continuing. +/// +/// # Warning +/// +/// On some systems, calling [`wait`] or similar is necessary for the OS to +/// release resources. A process that terminated but has not been waited on is +/// still around as a "zombie". Leaving too many zombies around may exhaust +/// global resources (for example process IDs). +/// +/// The standard library does *not* automatically wait on child processes (not +/// even if the `Child` is dropped), it is up to the application developer to do +/// so. As a consequence, dropping `Child` handles without waiting on them first +/// is not recommended in long-running applications. +/// +/// # Examples +/// +/// ```should_panic +/// use std::process::Command; +/// +/// let mut child = Command::new("/bin/cat") +/// .arg("file.txt") +/// .spawn() +/// .expect("failed to execute child"); +/// +/// let ecode = child.wait().expect("failed to wait on child"); +/// +/// assert!(ecode.success()); +/// ``` +/// +/// [`wait`]: Child::wait +#[stable(feature = "process", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Child")] +pub struct Child { + pub(crate) handle: imp::Process, + + /// The handle for writing to the child's standard input (stdin), if it + /// has been captured. You might find it helpful to do + /// + /// ```ignore (incomplete) + /// let stdin = child.stdin.take().expect("handle present"); + /// ``` + /// + /// to avoid partially moving the `child` and thus blocking yourself from calling + /// functions on `child` while using `stdin`. + #[stable(feature = "process", since = "1.0.0")] + pub stdin: Option, + + /// The handle for reading from the child's standard output (stdout), if it + /// has been captured. You might find it helpful to do + /// + /// ```ignore (incomplete) + /// let stdout = child.stdout.take().expect("handle present"); + /// ``` + /// + /// to avoid partially moving the `child` and thus blocking yourself from calling + /// functions on `child` while using `stdout`. + #[stable(feature = "process", since = "1.0.0")] + pub stdout: Option, + + /// The handle for reading from the child's standard error (stderr), if it + /// has been captured. You might find it helpful to do + /// + /// ```ignore (incomplete) + /// let stderr = child.stderr.take().expect("handle present"); + /// ``` + /// + /// to avoid partially moving the `child` and thus blocking yourself from calling + /// functions on `child` while using `stderr`. + #[stable(feature = "process", since = "1.0.0")] + pub stderr: Option, +} + +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for Child {} + +impl AsInner for Child { + #[inline] + fn as_inner(&self) -> &imp::Process { + &self.handle } } -#[lang = "termination"] +impl FromInner<(imp::Process, StdioPipes)> for Child { + fn from_inner((handle, io): (imp::Process, StdioPipes)) -> Child { + Child { + handle, + stdin: io.stdin.map(ChildStdin::from_inner), + stdout: io.stdout.map(ChildStdout::from_inner), + stderr: io.stderr.map(ChildStderr::from_inner), + } + } +} + +impl IntoInner for Child { + fn into_inner(self) -> imp::Process { + self.handle + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Child { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Child") + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .finish_non_exhaustive() + } +} + +/// The pipes connected to a spawned process. +/// +/// Used to pass pipe handles between this module and [`imp`]. +pub(crate) struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +/// A handle to a child process's standard input (stdin). +/// +/// This struct is used in the [`stdin`] field on [`Child`]. +/// +/// When an instance of `ChildStdin` is [dropped], the `ChildStdin`'s underlying +/// file handle will be closed. If the child process was blocked on input prior +/// to being dropped, it will become unblocked after dropping. +/// +/// [`stdin`]: Child::stdin +/// [dropped]: Drop +#[stable(feature = "process", since = "1.0.0")] +pub struct ChildStdin { + inner: imp::ChildPipe, +} + +// In addition to the `impl`s here, `ChildStdin` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + +#[stable(feature = "process", since = "1.0.0")] +impl Write for ChildStdin { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&*self).write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&*self).write_vectored(bufs) + } + + fn is_write_vectored(&self) -> bool { + io::Write::is_write_vectored(&&*self) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&*self).flush() + } +} + +#[stable(feature = "write_mt", since = "1.48.0")] +impl Write for &ChildStdin { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl AsInner for ChildStdin { + #[inline] + fn as_inner(&self) -> &imp::ChildPipe { + &self.inner + } +} + +impl IntoInner for ChildStdin { + fn into_inner(self) -> imp::ChildPipe { + self.inner + } +} + +impl FromInner for ChildStdin { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdin { + ChildStdin { inner: pipe } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ChildStdin { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ChildStdin").finish_non_exhaustive() + } +} + +/// A handle to a child process's standard output (stdout). +/// +/// This struct is used in the [`stdout`] field on [`Child`]. +/// +/// When an instance of `ChildStdout` is [dropped], the `ChildStdout`'s +/// underlying file handle will be closed. +/// +/// [`stdout`]: Child::stdout +/// [dropped]: Drop +#[stable(feature = "process", since = "1.0.0")] +pub struct ChildStdout { + inner: imp::ChildPipe, +} + +// In addition to the `impl`s here, `ChildStdout` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + +#[stable(feature = "process", since = "1.0.0")] +impl Read for ChildStdout { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.inner.read_to_end(buf) + } +} + +impl AsInner for ChildStdout { + #[inline] + fn as_inner(&self) -> &imp::ChildPipe { + &self.inner + } +} + +impl IntoInner for ChildStdout { + fn into_inner(self) -> imp::ChildPipe { + self.inner + } +} + +impl FromInner for ChildStdout { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdout { + ChildStdout { inner: pipe } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ChildStdout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ChildStdout").finish_non_exhaustive() + } +} + +/// A handle to a child process's stderr. +/// +/// This struct is used in the [`stderr`] field on [`Child`]. +/// +/// When an instance of `ChildStderr` is [dropped], the `ChildStderr`'s +/// underlying file handle will be closed. +/// +/// [`stderr`]: Child::stderr +/// [dropped]: Drop +#[stable(feature = "process", since = "1.0.0")] +pub struct ChildStderr { + inner: imp::ChildPipe, +} + +// In addition to the `impl`s here, `ChildStderr` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + +#[stable(feature = "process", since = "1.0.0")] +impl Read for ChildStderr { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.inner.read_to_end(buf) + } +} + +impl AsInner for ChildStderr { + #[inline] + fn as_inner(&self) -> &imp::ChildPipe { + &self.inner + } +} + +impl IntoInner for ChildStderr { + fn into_inner(self) -> imp::ChildPipe { + self.inner + } +} + +impl FromInner for ChildStderr { + fn from_inner(pipe: imp::ChildPipe) -> ChildStderr { + ChildStderr { inner: pipe } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for ChildStderr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ChildStderr").finish_non_exhaustive() + } +} + +/// A process builder, providing fine-grained control +/// over how a new process should be spawned. +/// +/// A default configuration can be +/// generated using `Command::new(program)`, where `program` gives a path to the +/// program to be executed. Additional builder methods allow the configuration +/// to be changed (for example, by adding arguments) prior to spawning: +/// +/// ``` +/// # if cfg!(not(all(target_vendor = "apple", not(target_os = "macos")))) { +/// use std::process::Command; +/// +/// let output = if cfg!(target_os = "windows") { +/// Command::new("cmd") +/// .args(["/C", "echo hello"]) +/// .output() +/// .expect("failed to execute process") +/// } else { +/// Command::new("sh") +/// .arg("-c") +/// .arg("echo hello") +/// .output() +/// .expect("failed to execute process") +/// }; +/// +/// let hello = output.stdout; +/// # } +/// ``` +/// +/// `Command` can be reused to spawn multiple processes. The builder methods +/// change the command without needing to immediately spawn the process. +/// +/// ```no_run +/// use std::process::Command; +/// +/// let mut echo_hello = Command::new("sh"); +/// echo_hello.arg("-c").arg("echo hello"); +/// let hello_1 = echo_hello.output().expect("failed to execute process"); +/// let hello_2 = echo_hello.output().expect("failed to execute process"); +/// ``` +/// +/// Similarly, you can call builder methods after spawning a process and then +/// spawn a new process with the modified settings. +/// +/// ```no_run +/// use std::process::Command; +/// +/// let mut list_dir = Command::new("ls"); +/// +/// // Execute `ls` in the current directory of the program. +/// list_dir.status().expect("process failed to execute"); +/// +/// println!(); +/// +/// // Change `ls` to execute in the root directory. +/// list_dir.current_dir("/"); +/// +/// // And then execute `ls` again but in the root directory. +/// list_dir.status().expect("process failed to execute"); +/// ``` +#[stable(feature = "process", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Command")] +pub struct Command { + inner: imp::Command, +} + +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for Command {} + +impl Command { + /// Constructs a new `Command` for launching the program at + /// path `program`, with the following default configuration: + /// + /// * No arguments to the program + /// * Inherit the current process's environment + /// * Inherit the current process's working directory + /// * Inherit stdin/stdout/stderr for [`spawn`] or [`status`], but create pipes for [`output`] + /// + /// [`spawn`]: Self::spawn + /// [`status`]: Self::status + /// [`output`]: Self::output + /// + /// Builder methods are provided to change these defaults and + /// otherwise configure the process. + /// + /// If `program` is not an absolute path, the `PATH` will be searched in + /// an OS-defined way. + /// + /// The search path to be used may be controlled by setting the + /// `PATH` environment variable on the Command, + /// but this has some implementation limitations on Windows + /// (see issue #37519). + /// + /// # Platform-specific behavior + /// + /// Note on Windows: For executable files with the .exe extension, + /// it can be omitted when specifying the program for this Command. + /// However, if the file has a different extension, + /// a filename including the extension needs to be provided, + /// otherwise the file won't be found. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("sh") + /// .spawn() + /// .expect("sh command failed to start"); + /// ``` + /// + /// # Caveats + /// + /// [`Command::new`] is only intended to accept the path of the program. If you pass a program + /// path along with arguments like `Command::new("ls -l").spawn()`, it will try to search for + /// `ls -l` literally. The arguments need to be passed separately, such as via [`arg`] or + /// [`args`]. + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .arg("-l") // arg passed separately + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + /// + /// [`arg`]: Self::arg + /// [`args`]: Self::args + #[stable(feature = "process", since = "1.0.0")] + pub fn new>(program: S) -> Command { + Command { inner: imp::Command::new(program.as_ref()) } + } + + /// Adds an argument to pass to the program. + /// + /// Only one argument can be passed per use. So instead of: + /// + /// ```no_run + /// # std::process::Command::new("sh") + /// .arg("-C /path/to/repo") + /// # ; + /// ``` + /// + /// usage would be: + /// + /// ```no_run + /// # std::process::Command::new("sh") + /// .arg("-C") + /// .arg("/path/to/repo") + /// # ; + /// ``` + /// + /// To pass multiple arguments see [`args`]. + /// + /// [`args`]: Command::args + /// + /// Note that the argument is not passed through a shell, but given + /// literally to the program. This means that shell syntax like quotes, + /// escaped characters, word splitting, glob patterns, variable substitution, + /// etc. have no effect. + /// + ///
+ /// + /// On Windows, use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to + /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files + /// use a non-standard way of decoding arguments. They are therefore vulnerable + /// to malicious input. + /// + /// In the case of `cmd.exe` this is especially important because a malicious + /// argument can potentially run arbitrary shell commands. + /// + /// See [Windows argument splitting][windows-args] for more details + /// or [`raw_arg`] for manually implementing non-standard argument encoding. + /// + /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg + /// [windows-args]: crate::process#windows-argument-splitting + /// + ///
+ /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .arg("-l") + /// .arg("-a") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn arg>(&mut self, arg: S) -> &mut Command { + self.inner.arg(arg.as_ref()); + self + } + + /// Adds multiple arguments to pass to the program. + /// + /// To pass a single argument see [`arg`]. + /// + /// [`arg`]: Command::arg + /// + /// Note that the arguments are not passed through a shell, but given + /// literally to the program. This means that shell syntax like quotes, + /// escaped characters, word splitting, glob patterns, variable substitution, etc. + /// have no effect. + /// + ///
+ /// + /// On Windows, use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to + /// use with `arg`. However, some applications such as `cmd.exe` and `.bat` files + /// use a non-standard way of decoding arguments. They are therefore vulnerable + /// to malicious input. + /// + /// In the case of `cmd.exe` this is especially important because a malicious + /// argument can potentially run arbitrary shell commands. + /// + /// See [Windows argument splitting][windows-args] for more details + /// or [`raw_arg`] for manually implementing non-standard argument encoding. + /// + /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg + /// [windows-args]: crate::process#windows-argument-splitting + /// + ///
+ /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .args(["-l", "-a"]) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn args(&mut self, args: I) -> &mut Command + where + I: IntoIterator, + S: AsRef, + { + for arg in args { + self.arg(arg.as_ref()); + } + self + } + + /// Inserts or updates an explicit environment variable mapping. + /// + /// This method allows you to add an environment variable mapping to the spawned process or + /// overwrite a previously set value. You can use [`Command::envs`] to set multiple environment + /// variables simultaneously. + /// + /// Child processes will inherit environment variables from their parent process by default. + /// Environment variables explicitly set using [`Command::env`] take precedence over inherited + /// variables. You can disable environment variable inheritance entirely using + /// [`Command::env_clear`] or for a single key using [`Command::env_remove`]. + /// + /// Note that environment variable names are case-insensitive (but + /// case-preserving) on Windows and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .env("PATH", "/bin") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn env(&mut self, key: K, val: V) -> &mut Command + where + K: AsRef, + V: AsRef, + { + self.inner.env_mut().set(key.as_ref(), val.as_ref()); + self + } + + /// Inserts or updates multiple explicit environment variable mappings. + /// + /// This method allows you to add multiple environment variable mappings to the spawned process + /// or overwrite previously set values. You can use [`Command::env`] to set a single environment + /// variable. + /// + /// Child processes will inherit environment variables from their parent process by default. + /// Environment variables explicitly set using [`Command::envs`] take precedence over inherited + /// variables. You can disable environment variable inheritance entirely using + /// [`Command::env_clear`] or for a single key using [`Command::env_remove`]. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// use std::env; + /// use std::collections::HashMap; + /// + /// let filtered_env : HashMap = + /// env::vars().filter(|&(ref k, _)| + /// k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH" + /// ).collect(); + /// + /// Command::new("printenv") + /// .stdin(Stdio::null()) + /// .stdout(Stdio::inherit()) + /// .env_clear() + /// .envs(&filtered_env) + /// .spawn() + /// .expect("printenv failed to start"); + /// ``` + #[stable(feature = "command_envs", since = "1.19.0")] + pub fn envs(&mut self, vars: I) -> &mut Command + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + for (ref key, ref val) in vars { + self.inner.env_mut().set(key.as_ref(), val.as_ref()); + } + self + } + + /// Removes an explicitly set environment variable and prevents inheriting it from a parent + /// process. + /// + /// This method will remove the explicit value of an environment variable set via + /// [`Command::env`] or [`Command::envs`]. In addition, it will prevent the spawned child + /// process from inheriting that environment variable from its parent process. + /// + /// After calling [`Command::env_remove`], the value associated with its key from + /// [`Command::get_envs`] will be [`None`]. + /// + /// To clear all explicitly set environment variables and disable all environment variable + /// inheritance, you can use [`Command::env_clear`]. + /// + /// # Examples + /// + /// Prevent any inherited `GIT_DIR` variable from changing the target of the `git` command, + /// while allowing all other variables, like `GIT_AUTHOR_NAME`. + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("git") + /// .arg("commit") + /// .env_remove("GIT_DIR") + /// .spawn()?; + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn env_remove>(&mut self, key: K) -> &mut Command { + self.inner.env_mut().remove(key.as_ref()); + self + } + + /// Clears all explicitly set environment variables and prevents inheriting any parent process + /// environment variables. + /// + /// This method will remove all explicitly added environment variables set via [`Command::env`] + /// or [`Command::envs`]. In addition, it will prevent the spawned child process from inheriting + /// any environment variable from its parent process. + /// + /// After calling [`Command::env_clear`], the iterator from [`Command::get_envs`] will be + /// empty. + /// + /// You can use [`Command::env_remove`] to clear a single mapping. + /// + /// # Examples + /// + /// The behavior of `sort` is affected by `LANG` and `LC_*` environment variables. + /// Clearing the environment makes `sort`'s behavior independent of the parent processes' language. + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("sort") + /// .arg("file.txt") + /// .env_clear() + /// .spawn()?; + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn env_clear(&mut self) -> &mut Command { + self.inner.env_mut().clear(); + self + } + + /// Sets the working directory for the child process. + /// + /// # Platform-specific behavior + /// + /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous + /// whether it should be interpreted relative to the parent's working + /// directory or relative to `current_dir`. The behavior in this case is + /// platform specific and unstable, and it's recommended to use + /// [`canonicalize`] to get an absolute program path instead. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .current_dir("/bin") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + /// + /// [`canonicalize`]: crate::fs::canonicalize + #[stable(feature = "process", since = "1.0.0")] + pub fn current_dir>(&mut self, dir: P) -> &mut Command { + self.inner.cwd(dir.as_ref().as_ref()); + self + } + + /// Configuration for the child process's standard input (stdin) handle. + /// + /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and + /// defaults to [`piped`] when used with [`output`]. + /// + /// [`inherit`]: Stdio::inherit + /// [`piped`]: Stdio::piped + /// [`spawn`]: Self::spawn + /// [`status`]: Self::status + /// [`output`]: Self::output + /// + /// # Examples + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stdin(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn stdin>(&mut self, cfg: T) -> &mut Command { + self.inner.stdin(cfg.into().0); + self + } + + /// Configuration for the child process's standard output (stdout) handle. + /// + /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and + /// defaults to [`piped`] when used with [`output`]. + /// + /// [`inherit`]: Stdio::inherit + /// [`piped`]: Stdio::piped + /// [`spawn`]: Self::spawn + /// [`status`]: Self::status + /// [`output`]: Self::output + /// + /// # Examples + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stdout(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn stdout>(&mut self, cfg: T) -> &mut Command { + self.inner.stdout(cfg.into().0); + self + } + + /// Configuration for the child process's standard error (stderr) handle. + /// + /// Defaults to [`inherit`] when used with [`spawn`] or [`status`], and + /// defaults to [`piped`] when used with [`output`]. + /// + /// [`inherit`]: Stdio::inherit + /// [`piped`]: Stdio::piped + /// [`spawn`]: Self::spawn + /// [`status`]: Self::status + /// [`output`]: Self::output + /// + /// # Examples + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stderr(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn stderr>(&mut self, cfg: T) -> &mut Command { + self.inner.stderr(cfg.into().0); + self + } + + /// Executes the command as a child process, returning a handle to it. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn spawn(&mut self) -> io::Result { + self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) + } + + /// Executes the command as a child process, waiting for it to finish and + /// collecting all of its output. + /// + /// By default, stdout and stderr are captured (and used to provide the + /// resulting output). Stdin is not inherited from the parent and any + /// attempt by the child process to read from the stdin stream will result + /// in the stream immediately closing. + /// + /// # Examples + /// + /// ```should_panic + /// use std::process::Command; + /// use std::io::{self, Write}; + /// let output = Command::new("/bin/cat") + /// .arg("file.txt") + /// .output()?; + /// + /// println!("status: {}", output.status); + /// io::stdout().write_all(&output.stdout)?; + /// io::stderr().write_all(&output.stderr)?; + /// + /// assert!(output.status.success()); + /// # io::Result::Ok(()) + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn output(&mut self) -> io::Result { + let (status, stdout, stderr) = imp::output(&mut self.inner)?; + Ok(Output { status: ExitStatus(status), stdout, stderr }) + } + + /// Executes a command as a child process, waiting for it to finish and + /// collecting its status. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// + /// # Examples + /// + /// ```should_panic + /// use std::process::Command; + /// + /// let status = Command::new("/bin/cat") + /// .arg("file.txt") + /// .status() + /// .expect("failed to execute process"); + /// + /// println!("process finished with: {status}"); + /// + /// assert!(status.success()); + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn status(&mut self) -> io::Result { + self.inner + .spawn(imp::Stdio::Inherit, true) + .map(Child::from_inner) + .and_then(|mut p| p.wait()) + } + + /// Returns the path to the program that was given to [`Command::new`]. + /// + /// # Examples + /// + /// ``` + /// use std::process::Command; + /// + /// let cmd = Command::new("echo"); + /// assert_eq!(cmd.get_program(), "echo"); + /// ``` + #[must_use] + #[stable(feature = "command_access", since = "1.57.0")] + pub fn get_program(&self) -> &OsStr { + self.inner.get_program() + } + + /// Returns an iterator of the arguments that will be passed to the program. + /// + /// This does not include the path to the program as the first argument; + /// it only includes the arguments specified with [`Command::arg`] and + /// [`Command::args`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use std::process::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.arg("first").arg("second"); + /// let args: Vec<&OsStr> = cmd.get_args().collect(); + /// assert_eq!(args, &["first", "second"]); + /// ``` + #[stable(feature = "command_access", since = "1.57.0")] + pub fn get_args(&self) -> CommandArgs<'_> { + CommandArgs { inner: self.inner.get_args() } + } + + /// Returns an iterator of the environment variables explicitly set for the child process. + /// + /// Environment variables explicitly set using [`Command::env`], [`Command::envs`], and + /// [`Command::env_remove`] can be retrieved with this method. + /// + /// Note that this output does not include environment variables inherited from the parent + /// process. + /// + /// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value + /// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for + /// the [`None`] value will no longer inherit from its parent process. + /// + /// An empty iterator can indicate that no explicit mappings were added or that + /// [`Command::env_clear`] was called. After calling [`Command::env_clear`], the child process + /// will not inherit any environment variables from its parent process. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use std::process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env("TERM", "dumb").env_remove("TZ"); + /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect(); + /// assert_eq!(envs, &[ + /// (OsStr::new("TERM"), Some(OsStr::new("dumb"))), + /// (OsStr::new("TZ"), None) + /// ]); + /// ``` + #[stable(feature = "command_access", since = "1.57.0")] + pub fn get_envs(&self) -> CommandEnvs<'_> { + CommandEnvs { iter: self.inner.get_envs() } + } + + /// Returns the working directory for the child process. + /// + /// This returns [`None`] if the working directory will not be changed. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// use std::process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_current_dir(), None); + /// cmd.current_dir("/bin"); + /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin"))); + /// ``` + #[must_use] + #[stable(feature = "command_access", since = "1.57.0")] + pub fn get_current_dir(&self) -> Option<&Path> { + self.inner.get_current_dir() + } + + /// Returns whether the environment will be cleared for the child process. + /// + /// This returns `true` if [`Command::env_clear`] was called, and `false` otherwise. + /// When `true`, the child process will not inherit any environment variables from + /// its parent process. + /// + /// # Examples + /// + /// ``` + /// #![feature(command_resolved_envs)] + /// use std::process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_env_clear(), false); + /// + /// cmd.env_clear(); + /// assert_eq!(cmd.get_env_clear(), true); + /// ``` + #[must_use] + #[unstable(feature = "command_resolved_envs", issue = "149070")] + pub fn get_env_clear(&self) -> bool { + self.inner.get_env_clear() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Command { + /// Format the program and arguments of a Command for display. Any + /// non-utf8 data is lossily converted using the utf8 replacement + /// character. + /// + /// The default format approximates a shell invocation of the program along with its + /// arguments. It does not include most of the other command properties. The output is not guaranteed to work + /// (e.g. due to lack of shell-escaping or differences in path resolution). + /// On some platforms you can use [the alternate syntax] to show more fields. + /// + /// Note that the debug implementation is platform-specific. + /// + /// [the alternate syntax]: fmt#sign0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl AsInner for Command { + #[inline] + fn as_inner(&self) -> &imp::Command { + &self.inner + } +} + +impl AsInnerMut for Command { + #[inline] + fn as_inner_mut(&mut self) -> &mut imp::Command { + &mut self.inner + } +} + +/// An iterator over the command arguments. +/// +/// This struct is created by [`Command::get_args`]. See its documentation for +/// more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "command_access", since = "1.57.0")] +#[derive(Debug)] +pub struct CommandArgs<'a> { + inner: imp::CommandArgs<'a>, +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.inner.len() + } + fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +/// An iterator over the command environment variables. +/// +/// This struct is created by +/// [`Command::get_envs`][crate::process::Command::get_envs]. See its +/// documentation for more. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[stable(feature = "command_access", since = "1.57.0")] +pub struct CommandEnvs<'a> { + iter: imp::CommandEnvs<'a>, +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> Iterator for CommandEnvs<'a> { + type Item = (&'a OsStr, Option<&'a OsStr>); + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> ExactSizeIterator for CommandEnvs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "command_access", since = "1.57.0")] +impl<'a> fmt::Debug for CommandEnvs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.iter.fmt(f) + } +} + +/// The output of a finished process. +/// +/// This is returned in a Result by either the [`output`] method of a +/// [`Command`], or the [`wait_with_output`] method of a [`Child`] +/// process. +/// +/// [`output`]: Command::output +/// [`wait_with_output`]: Child::wait_with_output +#[derive(PartialEq, Eq, Clone)] +#[stable(feature = "process", since = "1.0.0")] +pub struct Output { + /// The status (exit code) of the process. + #[stable(feature = "process", since = "1.0.0")] + pub status: ExitStatus, + /// The data that the process wrote to stdout. + #[stable(feature = "process", since = "1.0.0")] + pub stdout: Vec, + /// The data that the process wrote to stderr. + #[stable(feature = "process", since = "1.0.0")] + pub stderr: Vec, +} + +impl Output { + /// Returns an error if a nonzero exit status was received. + /// + /// If the [`Command`] exited successfully, + /// `self` is returned. + /// + /// This is equivalent to calling [`exit_ok`](ExitStatus::exit_ok) + /// on [`Output.status`](Output::status). + /// + /// Note that this will throw away the [`Output::stderr`] field in the error case. + /// If the child process outputs useful informantion to stderr, you can: + /// * Use `cmd.stderr(Stdio::inherit())` to forward the + /// stderr child process to the parent's stderr, + /// usually printing it to console where the user can see it. + /// This is usually correct for command-line applications. + /// * Capture `stderr` using a custom error type. + /// This is usually correct for libraries. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { + /// use std::process::Command; + /// assert!(Command::new("false").output().unwrap().exit_ok().is_err()); + /// # } + /// ``` + #[unstable(feature = "exit_status_error", issue = "84908")] + pub fn exit_ok(self) -> Result { + self.status.exit_ok()?; + Ok(self) + } +} + +// If either stderr or stdout are valid utf8 strings it prints the valid +// strings, otherwise it prints the byte sequence instead +#[stable(feature = "process_output_debug", since = "1.7.0")] +impl fmt::Debug for Output { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let stdout_utf8 = str::from_utf8(&self.stdout); + let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { + Ok(ref s) => s, + Err(_) => &self.stdout, + }; + + let stderr_utf8 = str::from_utf8(&self.stderr); + let stderr_debug: &dyn fmt::Debug = match stderr_utf8 { + Ok(ref s) => s, + Err(_) => &self.stderr, + }; + + fmt.debug_struct("Output") + .field("status", &self.status) + .field("stdout", stdout_debug) + .field("stderr", stderr_debug) + .finish() + } +} + +/// Describes what to do with a standard I/O stream for a child process when +/// passed to the [`stdin`], [`stdout`], and [`stderr`] methods of [`Command`]. +/// +/// [`stdin`]: Command::stdin +/// [`stdout`]: Command::stdout +/// [`stderr`]: Command::stderr +#[stable(feature = "process", since = "1.0.0")] +pub struct Stdio(imp::Stdio); + +impl Stdio { + /// A new pipe should be arranged to connect the parent and child processes. + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), "Hello, world!\n"); + /// // Nothing echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::io::Write; + /// use std::process::{Command, Stdio}; + /// + /// let mut child = Command::new("rev") + /// .stdin(Stdio::piped()) + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("Failed to spawn child process"); + /// + /// let mut stdin = child.stdin.take().expect("Failed to open stdin"); + /// std::thread::spawn(move || { + /// stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin"); + /// }); + /// + /// let output = child.wait_with_output().expect("Failed to read stdout"); + /// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH"); + /// ``` + /// + /// Writing more than a pipe buffer's worth of input to stdin without also reading + /// stdout and stderr at the same time may cause a deadlock. + /// This is an issue when running any program that doesn't guarantee that it reads + /// its entire stdin before writing more than a pipe buffer's worth of output. + /// The size of a pipe buffer varies on different targets. + /// + #[must_use] + #[stable(feature = "process", since = "1.0.0")] + pub fn piped() -> Stdio { + Stdio(imp::Stdio::MakePipe) + } + + /// The child inherits from the corresponding parent descriptor. + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::inherit()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // "Hello, world!" echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// use std::io::{self, Write}; + /// + /// let output = Command::new("rev") + /// .stdin(Stdio::inherit()) + /// .stdout(Stdio::piped()) + /// .output()?; + /// + /// print!("You piped in the reverse of: "); + /// io::stdout().write_all(&output.stdout)?; + /// # io::Result::Ok(()) + /// ``` + #[must_use] + #[stable(feature = "process", since = "1.0.0")] + pub fn inherit() -> Stdio { + Stdio(imp::Stdio::Inherit) + } + + /// This stream will be ignored. This is the equivalent of attaching the + /// stream to `/dev/null`. + /// + /// # Examples + /// + /// With stdout: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::null()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // Nothing echoed to console + /// ``` + /// + /// With stdin: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// let output = Command::new("rev") + /// .stdin(Stdio::null()) + /// .stdout(Stdio::piped()) + /// .output() + /// .expect("Failed to execute command"); + /// + /// assert_eq!(String::from_utf8_lossy(&output.stdout), ""); + /// // Ignores any piped-in input + /// ``` + #[must_use] + #[stable(feature = "process", since = "1.0.0")] + pub fn null() -> Stdio { + Stdio(imp::Stdio::Null) + } + + /// Returns `true` if this requires [`Command`] to create a new pipe. + /// + /// # Example + /// + /// ``` + /// #![feature(stdio_makes_pipe)] + /// use std::process::Stdio; + /// + /// let io = Stdio::piped(); + /// assert_eq!(io.makes_pipe(), true); + /// ``` + #[unstable(feature = "stdio_makes_pipe", issue = "98288")] + pub fn makes_pipe(&self) -> bool { + matches!(self.0, imp::Stdio::MakePipe) + } +} + +impl FromInner for Stdio { + fn from_inner(inner: imp::Stdio) -> Stdio { + Stdio(inner) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Stdio { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Stdio").finish_non_exhaustive() + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + /// Converts a [`ChildStdin`] into a [`Stdio`]. + /// + /// # Examples + /// + /// `ChildStdin` will be converted to `Stdio` using `Stdio::from` under the hood. + /// + /// ```rust,no_run + /// use std::process::{Command, Stdio}; + /// + /// let reverse = Command::new("rev") + /// .stdin(Stdio::piped()) + /// .spawn() + /// .expect("failed reverse command"); + /// + /// let _echo = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(reverse.stdin.unwrap()) // Converted into a Stdio here + /// .output() + /// .expect("failed echo command"); + /// + /// // "!dlrow ,olleH" echoed to console + /// ``` + fn from(child: ChildStdin) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + /// Converts a [`ChildStdout`] into a [`Stdio`]. + /// + /// # Examples + /// + /// `ChildStdout` will be converted to `Stdio` using `Stdio::from` under the hood. + /// + /// ```rust,no_run + /// use std::process::{Command, Stdio}; + /// + /// let hello = Command::new("echo") + /// .arg("Hello, world!") + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("failed echo command"); + /// + /// let reverse = Command::new("rev") + /// .stdin(hello.stdout.unwrap()) // Converted into a Stdio here + /// .output() + /// .expect("failed reverse command"); + /// + /// assert_eq!(reverse.stdout, b"!dlrow ,olleH\n"); + /// ``` + fn from(child: ChildStdout) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +#[stable(feature = "stdio_from", since = "1.20.0")] +impl From for Stdio { + /// Converts a [`ChildStderr`] into a [`Stdio`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use std::process::{Command, Stdio}; + /// + /// let reverse = Command::new("rev") + /// .arg("non_existing_file.txt") + /// .stderr(Stdio::piped()) + /// .spawn() + /// .expect("failed reverse command"); + /// + /// let cat = Command::new("cat") + /// .arg("-") + /// .stdin(reverse.stderr.unwrap()) // Converted into a Stdio here + /// .output() + /// .expect("failed echo command"); + /// + /// assert_eq!( + /// String::from_utf8_lossy(&cat.stdout), + /// "rev: cannot open non_existing_file.txt: No such file or directory\n" + /// ); + /// ``` + fn from(child: ChildStderr) -> Stdio { + Stdio::from_inner(child.into_inner().into()) + } +} + +// #[stable(feature = "stdio_from", since = "1.20.0")] +// impl From for Stdio { +// /// Converts a [`File`](fs::File) into a [`Stdio`]. +// /// +// /// # Examples +// /// +// /// `File` will be converted to `Stdio` using `Stdio::from` under the hood. +// /// +// /// ```rust,no_run +// /// use std::fs::File; +// /// use std::process::Command; +// /// +// /// // With the `foo.txt` file containing "Hello, world!" +// /// let file = File::open("foo.txt")?; +// /// +// /// let reverse = Command::new("rev") +// /// .stdin(file) // Implicit File conversion into a Stdio +// /// .output()?; +// /// +// /// assert_eq!(reverse.stdout, b"!dlrow ,olleH"); +// /// # std::io::Result::Ok(()) +// /// ``` +// fn from(file: fs::File) -> Stdio { +// Stdio::from_inner(file.into_inner().into()) +// } +// } + +#[stable(feature = "stdio_from_stdio", since = "1.74.0")] +impl From for Stdio { + /// Redirect command stdout/stderr to our stdout + /// + /// # Examples + /// + /// ```rust + /// #![feature(exit_status_error)] + /// use std::io; + /// use std::process::Command; + /// + /// # fn test() -> Result<(), Box> { + /// let output = Command::new("whoami") + // "whoami" is a command which exists on both Unix and Windows, + // and which succeeds, producing some stdout output but no stderr. + /// .stdout(io::stdout()) + /// .output()?; + /// output.status.exit_ok()?; + /// assert!(output.stdout.is_empty()); + /// # Ok(()) + /// # } + /// # + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { + /// # test().unwrap(); + /// # } + /// ``` + fn from(inherit: io::Stdout) -> Stdio { + Stdio::from_inner(inherit.into()) + } +} + +#[stable(feature = "stdio_from_stdio", since = "1.74.0")] +impl From for Stdio { + /// Redirect command stdout/stderr to our stderr + /// + /// # Examples + /// + /// ```rust + /// #![feature(exit_status_error)] + /// use std::io; + /// use std::process::Command; + /// + /// # fn test() -> Result<(), Box> { + /// let output = Command::new("whoami") + /// .stdout(io::stderr()) + /// .output()?; + /// output.status.exit_ok()?; + /// assert!(output.stdout.is_empty()); + /// # Ok(()) + /// # } + /// # + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { + /// # test().unwrap(); + /// # } + /// ``` + fn from(inherit: io::Stderr) -> Stdio { + Stdio::from_inner(inherit.into()) + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for Stdio { + fn from(pipe: io::PipeWriter) -> Self { + Stdio::from_inner(pipe.into_inner().into()) + } +} + +#[stable(feature = "anonymous_pipe", since = "1.87.0")] +impl From for Stdio { + fn from(pipe: io::PipeReader) -> Self { + Stdio::from_inner(pipe.into_inner().into()) + } +} + +/// Describes the result of a process after it has terminated. +/// +/// This `struct` is used to represent the exit status or other termination of a child process. +/// Child processes are created via the [`Command`] struct and their exit +/// status is exposed through the [`status`] method, or the [`wait`] method +/// of a [`Child`] process. +/// +/// An `ExitStatus` represents every possible disposition of a process. On Unix this +/// is the **wait status**. It is *not* simply an *exit status* (a value passed to `exit`). +/// +/// For proper error reporting of failed processes, print the value of `ExitStatus` or +/// `ExitStatusError` using their implementations of [`Display`](crate::fmt::Display). +/// +/// # Differences from `ExitCode` +/// +/// [`ExitCode`] is intended for terminating the currently running process, via +/// the `Termination` trait, in contrast to `ExitStatus`, which represents the +/// termination of a child process. These APIs are separate due to platform +/// compatibility differences and their expected usage; it is not generally +/// possible to exactly reproduce an `ExitStatus` from a child for the current +/// process after the fact. +/// +/// [`status`]: Command::status +/// [`wait`]: Child::wait +// +// We speak slightly loosely (here and in various other places in the stdlib docs) about `exit` +// vs `_exit`. Naming of Unix system calls is not standardised across Unices, so terminology is a +// matter of convention and tradition. For clarity we usually speak of `exit`, even when we might +// mean an underlying system call such as `_exit`. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "process", since = "1.0.0")] +pub struct ExitStatus(imp::ExitStatus); + +/// The default value is one which indicates successful completion. +#[stable(feature = "process_exitstatus_default", since = "1.73.0")] +impl Default for ExitStatus { + fn default() -> Self { + // Ideally this would be done by ExitCode::default().into() but that is complicated. + ExitStatus::from_inner(imp::ExitStatus::default()) + } +} + +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for ExitStatus {} + +impl ExitStatus { + /// Was termination successful? Returns a `Result`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// # if cfg!(all(unix, not(all(target_vendor = "apple", not(target_os = "macos"))))) { + /// use std::process::Command; + /// + /// let status = Command::new("ls") + /// .arg("/dev/nonexistent") + /// .status() + /// .expect("ls could not be executed"); + /// + /// println!("ls: {status}"); + /// status.exit_ok().expect_err("/dev/nonexistent could be listed!"); + /// # } // cfg!(unix) + /// ``` + #[unstable(feature = "exit_status_error", issue = "84908")] + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + self.0.exit_ok().map_err(ExitStatusError) + } + + /// Was termination successful? Signal termination is not considered a + /// success, and success is defined as a zero exit status. + /// + /// # Examples + /// + /// ```rust,no_run + /// use std::process::Command; + /// + /// let status = Command::new("mkdir") + /// .arg("projects") + /// .status() + /// .expect("failed to execute mkdir"); + /// + /// if status.success() { + /// println!("'projects/' directory created"); + /// } else { + /// println!("failed to create 'projects/' directory: {status}"); + /// } + /// ``` + #[must_use] + #[stable(feature = "process", since = "1.0.0")] + pub fn success(&self) -> bool { + self.0.exit_ok().is_ok() + } + + /// Returns the exit code of the process, if any. + /// + /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the + /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8 + /// bits, and that values that didn't come from a program's call to `exit` may be invented by the + /// runtime system (often, for example, 255, 254, 127 or 126). + /// + /// On Unix, this will return `None` if the process was terminated by a signal. + /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) is an + /// extension trait for extracting any such signal, and other details, from the `ExitStatus`. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// let status = Command::new("mkdir") + /// .arg("projects") + /// .status() + /// .expect("failed to execute mkdir"); + /// + /// match status.code() { + /// Some(code) => println!("Exited with status code: {code}"), + /// None => println!("Process terminated by signal") + /// } + /// ``` + #[must_use] + #[stable(feature = "process", since = "1.0.0")] + pub fn code(&self) -> Option { + self.0.code() + } +} + +impl AsInner for ExitStatus { + #[inline] + fn as_inner(&self) -> &imp::ExitStatus { + &self.0 + } +} + +impl FromInner for ExitStatus { + fn from_inner(s: imp::ExitStatus) -> ExitStatus { + ExitStatus(s) + } +} + +#[stable(feature = "process", since = "1.0.0")] +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for ExitStatusError {} + +/// Describes the result of a process after it has failed +/// +/// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`]. +/// +/// # Examples +/// +/// ``` +/// #![feature(exit_status_error)] +/// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { +/// use std::process::{Command, ExitStatusError}; +/// +/// fn run(cmd: &str) -> Result<(), ExitStatusError> { +/// Command::new(cmd).status().unwrap().exit_ok()?; +/// Ok(()) +/// } +/// +/// run("true").unwrap(); +/// run("false").unwrap_err(); +/// # } // cfg!(unix) +/// ``` +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[unstable(feature = "exit_status_error", issue = "84908")] +// The definition of imp::ExitStatusError should ideally be such that +// Result<(), imp::ExitStatusError> has an identical representation to imp::ExitStatus. +pub struct ExitStatusError(imp::ExitStatusError); + +#[unstable(feature = "exit_status_error", issue = "84908")] +impl ExitStatusError { + /// Reports the exit code, if applicable, from an `ExitStatusError`. + /// + /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the + /// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8 + /// bits, and that values that didn't come from a program's call to `exit` may be invented by the + /// runtime system (often, for example, 255, 254, 127 or 126). + /// + /// On Unix, this will return `None` if the process was terminated by a signal. If you want to + /// handle such situations specially, consider using methods from + /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt). + /// + /// If the process finished by calling `exit` with a nonzero value, this will return + /// that exit status. + /// + /// If the error was something else, it will return `None`. + /// + /// If the process exited successfully (ie, by calling `exit(0)`), there is no + /// `ExitStatusError`. So the return value from `ExitStatusError::code()` is always nonzero. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { + /// use std::process::Command; + /// + /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err(); + /// assert_eq!(bad.code(), Some(1)); + /// # } // #[cfg(unix)] + /// ``` + #[must_use] + pub fn code(&self) -> Option { + self.code_nonzero().map(Into::into) + } + + /// Reports the exit code, if applicable, from an `ExitStatusError`, as a [`NonZero`]. + /// + /// This is exactly like [`code()`](Self::code), except that it returns a [NonZero]<[i32]>. + /// + /// Plain `code`, returning a plain integer, is provided because it is often more convenient. + /// The returned value from `code()` is indeed also nonzero; use `code_nonzero()` when you want + /// a type-level guarantee of nonzeroness. + /// + /// # Examples + /// + /// ``` + /// #![feature(exit_status_error)] + /// + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { + /// use std::num::NonZero; + /// use std::process::Command; + /// + /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err(); + /// assert_eq!(bad.code_nonzero().unwrap(), NonZero::new(1).unwrap()); + /// # } // cfg!(unix) + /// ``` + #[must_use] + pub fn code_nonzero(&self) -> Option> { + self.0.code() + } + + /// Converts an `ExitStatusError` (back) to an `ExitStatus`. + #[must_use] + pub fn into_status(&self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +#[unstable(feature = "exit_status_error", issue = "84908")] +impl From for ExitStatus { + fn from(error: ExitStatusError) -> Self { + Self(error.0.into()) + } +} + +#[unstable(feature = "exit_status_error", issue = "84908")] +impl fmt::Display for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "process exited unsuccessfully: {}", self.into_status()) + } +} + +#[unstable(feature = "exit_status_error", issue = "84908")] +impl crate::error::Error for ExitStatusError {} + +/// This type represents the status code the current process can return +/// to its parent under normal termination. +/// +/// `ExitCode` is intended to be consumed only by the standard library (via +/// [`Termination::report()`]). For forwards compatibility with potentially +/// unusual targets, this type currently does not provide `Eq`, `Hash`, or +/// access to the raw value. This type does provide `PartialEq` for +/// comparison, but note that there may potentially be multiple failure +/// codes, some of which will _not_ compare equal to `ExitCode::FAILURE`. +/// The standard library provides the canonical `SUCCESS` and `FAILURE` +/// exit codes as well as `From for ExitCode` for constructing other +/// arbitrary exit codes. +/// +/// # Portability +/// +/// Numeric values used in this type don't have portable meanings, and +/// different platforms may mask different amounts of them. +/// +/// For the platform's canonical successful and unsuccessful codes, see +/// the [`SUCCESS`] and [`FAILURE`] associated items. +/// +/// [`SUCCESS`]: ExitCode::SUCCESS +/// [`FAILURE`]: ExitCode::FAILURE +/// +/// # Differences from `ExitStatus` +/// +/// `ExitCode` is intended for terminating the currently running process, via +/// the `Termination` trait, in contrast to [`ExitStatus`], which represents the +/// termination of a child process. These APIs are separate due to platform +/// compatibility differences and their expected usage; it is not generally +/// possible to exactly reproduce an `ExitStatus` from a child for the current +/// process after the fact. +/// +/// # Examples +/// +/// `ExitCode` can be returned from the `main` function of a crate, as it implements +/// [`Termination`]: +/// +/// ``` +/// use std::process::ExitCode; +/// # fn check_foo() -> bool { true } +/// +/// fn main() -> ExitCode { +/// if !check_foo() { +/// return ExitCode::from(42); +/// } +/// +/// ExitCode::SUCCESS +/// } +/// ``` +#[derive(Clone, Copy, Debug, PartialEq)] +#[stable(feature = "process_exitcode", since = "1.61.0")] +pub struct ExitCode(imp::ExitCode); + +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl crate::sealed::Sealed for ExitCode {} + +#[stable(feature = "process_exitcode", since = "1.61.0")] +impl ExitCode { + /// The canonical `ExitCode` for successful termination on this platform. + /// + /// Note that a `()`-returning `main` implicitly results in a successful + /// termination, so there's no need to return this from `main` unless + /// you're also returning other possible codes. + #[stable(feature = "process_exitcode", since = "1.61.0")] + pub const SUCCESS: ExitCode = ExitCode(imp::ExitCode::SUCCESS); + + /// The canonical `ExitCode` for unsuccessful termination on this platform. + /// + /// If you're only returning this and `SUCCESS` from `main`, consider + /// instead returning `Err(_)` and `Ok(())` respectively, which will + /// return the same codes (but will also `eprintln!` the error). + #[stable(feature = "process_exitcode", since = "1.61.0")] + pub const FAILURE: ExitCode = ExitCode(imp::ExitCode::FAILURE); + + /// Exit the current process with the given `ExitCode`. + /// + /// Note that this has the same caveats as [`process::exit()`][exit], namely that this function + /// terminates the process immediately, so no destructors on the current stack or any other + /// thread's stack will be run. Also see those docs for some important notes on interop with C + /// code. If a clean shutdown is needed, it is recommended to simply return this ExitCode from + /// the `main` function, as demonstrated in the [type documentation](#examples). + /// + /// # Differences from `process::exit()` + /// + /// `process::exit()` accepts any `i32` value as the exit code for the process; however, there + /// are platforms that only use a subset of that value (see [`process::exit` platform-specific + /// behavior][exit#platform-specific-behavior]). `ExitCode` exists because of this; only + /// `ExitCode`s that are supported by a majority of our platforms can be created, so those + /// problems don't exist (as much) with this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(exitcode_exit_method)] + /// # use std::process::ExitCode; + /// # use std::fmt; + /// # enum UhOhError { GenericProblem, Specific, WithCode { exit_code: ExitCode, _x: () } } + /// # impl fmt::Display for UhOhError { + /// # fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } + /// # } + /// // there's no way to gracefully recover from an UhOhError, so we just + /// // print a message and exit + /// fn handle_unrecoverable_error(err: UhOhError) -> ! { + /// eprintln!("UH OH! {err}"); + /// let code = match err { + /// UhOhError::GenericProblem => ExitCode::FAILURE, + /// UhOhError::Specific => ExitCode::from(3), + /// UhOhError::WithCode { exit_code, .. } => exit_code, + /// }; + /// code.exit_process() + /// } + /// ``` + #[unstable(feature = "exitcode_exit_method", issue = "97100")] + pub fn exit_process(self) -> ! { + exit(self.to_i32()) + } +} + +impl ExitCode { + // This is private/perma-unstable because ExitCode is opaque; we don't know that i32 will serve + // all usecases, for example windows seems to use u32, unix uses the 8-15th bits of an i32, we + // likely want to isolate users anything that could restrict the platform specific + // representation of an ExitCode + // + // More info: https://internals.rust-lang.org/t/mini-pre-rfc-redesigning-process-exitstatus/5426 + /// Converts an `ExitCode` into an i32 + #[unstable( + feature = "process_exitcode_internals", + reason = "exposed only for libstd", + issue = "none" + )] + #[inline] + #[doc(hidden)] + pub fn to_i32(self) -> i32 { + self.0.as_i32() + } +} + +/// The default value is [`ExitCode::SUCCESS`] +#[stable(feature = "process_exitcode_default", since = "1.75.0")] +impl Default for ExitCode { + fn default() -> Self { + ExitCode::SUCCESS + } +} + +#[stable(feature = "process_exitcode", since = "1.61.0")] +impl From for ExitCode { + /// Constructs an `ExitCode` from an arbitrary u8 value. + fn from(code: u8) -> Self { + ExitCode(imp::ExitCode::from(code)) + } +} + +impl AsInner for ExitCode { + #[inline] + fn as_inner(&self) -> &imp::ExitCode { + &self.0 + } +} + +impl FromInner for ExitCode { + fn from_inner(s: imp::ExitCode) -> ExitCode { + ExitCode(s) + } +} + +impl Child { + /// Forces the child process to exit. If the child has already exited, `Ok(())` + /// is returned. + /// + /// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function. + /// + /// This is equivalent to sending a SIGKILL on Unix platforms. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("yes"); + /// if let Ok(mut child) = command.spawn() { + /// child.kill().expect("command couldn't be killed"); + /// } else { + /// println!("yes command didn't start"); + /// } + /// ``` + /// + /// [`ErrorKind`]: io::ErrorKind + /// [`InvalidInput`]: io::ErrorKind::InvalidInput + #[stable(feature = "process", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "child_kill")] + pub fn kill(&mut self) -> io::Result<()> { + self.handle.kill() + } + + /// Returns the OS-assigned process identifier associated with this child. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("ls"); + /// if let Ok(child) = command.spawn() { + /// println!("Child's ID is {}", child.id()); + /// } else { + /// println!("ls command didn't start"); + /// } + /// ``` + #[must_use] + #[stable(feature = "process_id", since = "1.3.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "child_id")] + pub fn id(&self) -> u32 { + self.handle.id() + } + + /// Waits for the child to exit completely, returning the status that it + /// exited with. This function will continue to have the same return value + /// after it has been called at least once. + /// + /// The stdin handle to the child process, if any, will be closed + /// before waiting. This helps avoid deadlock: it ensures that the + /// child does not block waiting for input from the parent, while + /// the parent waits for the child to exit. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("ls"); + /// if let Ok(mut child) = command.spawn() { + /// child.wait().expect("command wasn't running"); + /// println!("Child has finished its execution!"); + /// } else { + /// println!("ls command didn't start"); + /// } + /// ``` + #[stable(feature = "process", since = "1.0.0")] + pub fn wait(&mut self) -> io::Result { + drop(self.stdin.take()); + self.handle.wait().map(ExitStatus) + } + + /// Attempts to collect the exit status of the child if it has already + /// exited. + /// + /// This function will not block the calling thread and will only + /// check to see if the child process has exited or not. If the child has + /// exited then on Unix the process ID is reaped. This function is + /// guaranteed to repeatedly return a successful exit status so long as the + /// child has already exited. + /// + /// If the child has exited, then `Ok(Some(status))` is returned. If the + /// exit status is not available at this time then `Ok(None)` is returned. + /// If an error occurs, then that error is returned. + /// + /// Note that unlike `wait`, this function will not attempt to drop stdin. + /// + /// # Examples + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut child = Command::new("ls").spawn()?; + /// + /// match child.try_wait() { + /// Ok(Some(status)) => println!("exited with: {status}"), + /// Ok(None) => { + /// println!("status not ready yet, let's really wait"); + /// let res = child.wait(); + /// println!("result: {res:?}"); + /// } + /// Err(e) => println!("error attempting to wait: {e}"), + /// } + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "process_try_wait", since = "1.18.0")] + pub fn try_wait(&mut self) -> io::Result> { + Ok(self.handle.try_wait()?.map(ExitStatus)) + } + + /// Simultaneously waits for the child to exit and collect all remaining + /// output on the stdout/stderr handles, returning an `Output` + /// instance. + /// + /// The stdin handle to the child process, if any, will be closed + /// before waiting. This helps avoid deadlock: it ensures that the + /// child does not block waiting for input from the parent, while + /// the parent waits for the child to exit. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// In order to capture the output into this `Result` it is + /// necessary to create new pipes between parent and child. Use + /// `stdout(Stdio::piped())` or `stderr(Stdio::piped())`, respectively. + /// + /// # Examples + /// + /// ```should_panic + /// use std::process::{Command, Stdio}; + /// + /// let child = Command::new("/bin/cat") + /// .arg("file.txt") + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("failed to execute child"); + /// + /// let output = child + /// .wait_with_output() + /// .expect("failed to wait on child"); + /// + /// assert!(output.status.success()); + /// ``` + /// + #[stable(feature = "process", since = "1.0.0")] + pub fn wait_with_output(mut self) -> io::Result { + drop(self.stdin.take()); + + let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); + match (self.stdout.take(), self.stderr.take()) { + (None, None) => {} + (Some(mut out), None) => { + let res = out.read_to_end(&mut stdout); + res.unwrap(); + } + (None, Some(mut err)) => { + let res = err.read_to_end(&mut stderr); + res.unwrap(); + } + (Some(out), Some(err)) => { + let res = imp::read_output(out.inner, &mut stdout, err.inner, &mut stderr); + res.unwrap(); + } + } + + let status = self.wait()?; + Ok(Output { status, stdout, stderr }) + } +} + +/// Terminates the current process with the specified exit code. +/// +/// This function will never return and will immediately terminate the current +/// process. The exit code is passed through to the underlying OS and will be +/// available for consumption by another process. +/// +/// Note that because this function never returns, and that it terminates the +/// process, no destructors on the current stack or any other thread's stack +/// will be run. If a clean shutdown is needed it is recommended to only call +/// this function at a known point where there are no more destructors left +/// to run; or, preferably, simply return a type implementing [`Termination`] +/// (such as [`ExitCode`] or `Result`) from the `main` function and avoid this +/// function altogether: +/// +/// ``` +/// # use std::io::Error as MyError; +/// fn main() -> Result<(), MyError> { +/// // ... +/// Ok(()) +/// } +/// ``` +/// +/// In its current implementation, this function will execute exit handlers registered with `atexit` +/// as well as other platform-specific exit handlers (e.g. `fini` sections of ELF shared objects). +/// This means that Rust requires that all exit handlers are safe to execute at any time. In +/// particular, if an exit handler cleans up some state that might be concurrently accessed by other +/// threads, it is required that the exit handler performs suitable synchronization with those +/// threads. (The alternative to this requirement would be to not run exit handlers at all, which is +/// considered undesirable. Note that returning from `main` also calls `exit`, so making `exit` an +/// unsafe operation is not an option.) +/// +/// ## Platform-specific behavior +/// +/// **Unix**: On Unix-like platforms, it is unlikely that all 32 bits of `exit` +/// will be visible to a parent process inspecting the exit code. On most +/// Unix-like platforms, only the eight least-significant bits are considered. +/// +/// For example, the exit code for this example will be `0` on Linux, but `256` +/// on Windows: +/// +/// ```no_run +/// use std::process; +/// +/// process::exit(0x0100); +/// ``` +/// +/// ### Safe interop with C code +/// +/// On Unix, this function is currently implemented using the `exit` C function [`exit`][C-exit]. As +/// of C23, the C standard does not permit multiple threads to call `exit` concurrently. Rust +/// mitigates this with a lock, but if C code calls `exit`, that can still cause undefined behavior. +/// Note that returning from `main` is equivalent to calling `exit`. +/// +/// Therefore, it is undefined behavior to have two concurrent threads perform the following +/// without synchronization: +/// - One thread calls Rust's `exit` function or returns from Rust's `main` function +/// - Another thread calls the C function `exit` or `quick_exit`, or returns from C's `main` function +/// +/// Note that if a binary contains multiple copies of the Rust runtime (e.g., when combining +/// multiple `cdylib` or `staticlib`), they each have their own separate lock, so from the +/// perspective of code running in one of the Rust runtimes, the "outside" Rust code is basically C +/// code, and concurrent `exit` again causes undefined behavior. +/// +/// Individual C implementations might provide more guarantees than the standard and permit concurrent +/// calls to `exit`; consult the documentation of your C implementation for details. +/// +/// For some of the on-going discussion to make `exit` thread-safe in C, see: +/// - [Rust issue #126600](https://github.com/rust-lang/rust/issues/126600) +/// - [Austin Group Bugzilla (for POSIX)](https://austingroupbugs.net/view.php?id=1845) +/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=31997) +/// +/// [C-exit]: https://en.cppreference.com/w/c/program/exit +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "process_exit")] +pub fn exit(code: i32) -> ! { + crate::rt::cleanup(); + crate::sys::exit::exit(code) +} + +/// Terminates the process in an abnormal fashion. +/// +/// The function will never return and will immediately terminate the current +/// process in a platform specific "abnormal" manner. As a consequence, +/// no destructors on the current stack or any other thread's stack +/// will be run, Rust IO buffers (eg, from `BufWriter`) will not be flushed, +/// and C stdio buffers will (on most platforms) not be flushed. +/// +/// This is in contrast to the default behavior of [`panic!`] which unwinds +/// the current thread's stack and calls all destructors. +/// When `panic="abort"` is set, either as an argument to `rustc` or in a +/// crate's Cargo.toml, [`panic!`] and `abort` are similar. However, +/// [`panic!`] will still call the [panic hook] while `abort` will not. +/// +/// If a clean shutdown is needed it is recommended to only call +/// this function at a known point where there are no more destructors left +/// to run. +/// +/// The process's termination will be similar to that from the C `abort()` +/// function. On Unix, the process will terminate with signal `SIGABRT`, which +/// typically means that the shell prints "Aborted". +/// +/// # Examples +/// +/// ```no_run +/// use std::process; +/// +/// fn main() { +/// println!("aborting"); +/// +/// process::abort(); +/// +/// // execution never gets here +/// } +/// ``` +/// +/// The `abort` function terminates the process, so the destructor will not +/// get run on the example below: +/// +/// ```no_run +/// use std::process; +/// +/// struct HasDrop; +/// +/// impl Drop for HasDrop { +/// fn drop(&mut self) { +/// println!("This will never be printed!"); +/// } +/// } +/// +/// fn main() { +/// let _x = HasDrop; +/// process::abort(); +/// // the destructor implemented for HasDrop will never get run +/// } +/// ``` +/// +/// [panic hook]: crate::panic::set_hook +#[stable(feature = "process_abort", since = "1.17.0")] +#[cold] +#[cfg_attr(not(test), rustc_diagnostic_item = "process_abort")] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +pub fn abort() -> ! { + crate::sys::abort_internal(); +} + +/// Returns the OS-assigned process identifier associated with this process. +/// +/// # Examples +/// +/// ```no_run +/// use std::process; +/// +/// println!("My pid is {}", process::id()); +/// ``` +#[must_use] +#[stable(feature = "getpid", since = "1.26.0")] +pub fn id() -> u32 { + todo!() +} + +/// A trait for implementing arbitrary return types in the `main` function. +/// +/// The C-main function only supports returning integers. +/// So, every type implementing the `Termination` trait has to be converted +/// to an integer. +/// +/// The default implementations are returning `libc::EXIT_SUCCESS` to indicate +/// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned. +/// +/// Because different runtimes have different specifications on the return value +/// of the `main` function, this trait is likely to be available only on +/// standard library's runtime for convenience. Other runtimes are not required +/// to provide similar functionality. +#[cfg_attr(not(any(test, doctest)), lang = "termination")] +#[stable(feature = "termination_trait_lib", since = "1.61.0")] +#[rustc_on_unimplemented(on( + cause = "MainFunctionType", + message = "`main` has invalid return type `{Self}`", + label = "`main` can only return types that implement `{This}`" +))] pub trait Termination { /// Is called to get the representation of the value as status code. /// This status code is returned to the operating system. + #[stable(feature = "termination_trait_lib", since = "1.61.0")] fn report(self) -> ExitCode; } +#[stable(feature = "termination_trait_lib", since = "1.61.0")] impl Termination for () { #[inline] fn report(self) -> ExitCode { ExitCode::SUCCESS } } -impl Termination for isize { - #[inline] + +#[stable(feature = "termination_trait_lib", since = "1.61.0")] +impl Termination for ! { fn report(self) -> ExitCode { - ExitCode(self) + self } } -pub fn abort() -> ! { - loop {} +#[stable(feature = "termination_trait_lib", since = "1.61.0")] +impl Termination for Infallible { + fn report(self) -> ExitCode { + match self {} + } +} + +#[stable(feature = "termination_trait_lib", since = "1.61.0")] +impl Termination for ExitCode { + #[inline] + fn report(self) -> ExitCode { + self + } +} + +#[stable(feature = "termination_trait_lib", since = "1.61.0")] +impl Termination for Result { + fn report(self) -> ExitCode { + match self { + Ok(val) => val.report(), + Err(err) => { + io::attempt_print_to_stderr(format_args_nl!("Error: {err:?}")); + ExitCode::FAILURE + } + } + } } diff --git a/crates/std/src/rt.rs b/crates/std/src/rt.rs index a79dd3c..a57534c 100644 --- a/crates/std/src/rt.rs +++ b/crates/std/src/rt.rs @@ -1,8 +1,8 @@ macro_rules! rtabort { - ($($t:tt)*) => {{ - loop {} - }}; + ($($t:tt)*) => {{ loop {} }}; } macro_rules! rtprintpanic { ($($t:tt)*) => {{}}; } + +pub fn cleanup() {} diff --git a/crates/std/src/sys/alloc/mod.rs b/crates/std/src/sys/alloc/mod.rs index f352ec9..445dde8 100644 --- a/crates/std/src/sys/alloc/mod.rs +++ b/crates/std/src/sys/alloc/mod.rs @@ -107,7 +107,7 @@ cfg_select! { target_os = "zkvm" => { mod zkvm; } - target_os = "survos" => { - mod survos; + target_os = "survos" => { + mod survos; } } diff --git a/crates/std/src/sys/args/mod.rs b/crates/std/src/sys/args/mod.rs new file mode 100644 index 0000000..5424d40 --- /dev/null +++ b/crates/std/src/sys/args/mod.rs @@ -0,0 +1,60 @@ +//! Platform-dependent command line arguments abstraction. + +#![forbid(unsafe_op_in_unsafe_fn)] + +#[cfg(any( + all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), + target_family = "windows", + target_os = "hermit", + target_os = "motor", + target_os = "uefi", + target_os = "wasi", + target_os = "xous", +))] +mod common; + +cfg_select! { + any( + all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), + target_os = "hermit", + ) => { + mod unix; + pub use unix::*; + } + target_family = "windows" => { + mod windows; + pub use windows::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::*; + } + target_os = "motor" => { + mod motor; + pub use motor::*; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::*; + } + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::*; + } + all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { + mod wasip2; + pub use wasip2::*; + } + target_os = "xous" => { + mod xous; + pub use xous::*; + } + target_os = "zkvm" => { + mod zkvm; + pub use zkvm::*; + } + _ => { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/crates/std/src/sys/args/unsupported.rs b/crates/std/src/sys/args/unsupported.rs new file mode 100644 index 0000000..ecffc6d --- /dev/null +++ b/crates/std/src/sys/args/unsupported.rs @@ -0,0 +1,42 @@ +use crate::ffi::OsString; +use crate::fmt; + +pub struct Args {} + +pub fn args() -> Args { + Args {} +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().finish() + } +} + +impl Iterator for Args { + type Item = OsString; + + #[inline] + fn next(&mut self) -> Option { + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } +} + +impl DoubleEndedIterator for Args { + #[inline] + fn next_back(&mut self) -> Option { + None + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + 0 + } +} diff --git a/crates/std/src/sys/mod.rs b/crates/std/src/sys/mod.rs index 45ce3b4..05b3df0 100644 --- a/crates/std/src/sys/mod.rs +++ b/crates/std/src/sys/mod.rs @@ -1,20 +1,24 @@ +pub mod alloc; +pub mod args; +pub mod backtrace; pub mod cmath; pub mod configure_builtins; pub mod env; pub mod env_consts; pub mod exit; +pub mod io; pub mod os_str; +pub mod pal; pub mod path; +pub mod pipe; +pub mod process; +pub mod random; +pub mod stdio; pub mod sync; pub mod thread; -pub mod time; -pub mod random; pub mod thread_local; -pub mod alloc; -pub mod io; -pub mod pipe; -pub mod stdio; -pub mod backtrace; +pub mod time; +pub use pal::*; // pub mod fs; /// A trait for viewing representations from std types. @@ -38,3 +42,25 @@ pub(crate) trait IntoInner { pub(crate) trait FromInner { fn from_inner(inner: Inner) -> Self; } + +use crate::io as std_io; + +// SAFETY: must be called only once during runtime initialization. +// NOTE: this is not guaranteed to run, for example when Rust code is called externally. +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +pub fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> std_io::Error { + std_io::Error::UNSUPPORTED_PLATFORM +} + +pub fn abort_internal() -> ! { + core::intrinsics::abort(); +} diff --git a/crates/std/src/sys/pal/mod.rs b/crates/std/src/sys/pal/mod.rs new file mode 100644 index 0000000..88d9d42 --- /dev/null +++ b/crates/std/src/sys/pal/mod.rs @@ -0,0 +1,67 @@ +//! The PAL (platform abstraction layer) contains platform-specific abstractions +//! for implementing the features in the other submodules, such as e.g. bindings. + +#![allow(missing_debug_implementations)] + +cfg_select! { + unix => { + mod unix; + pub use self::unix::*; + } + windows => { + mod windows; + pub use self::windows::*; + } + target_os = "solid_asp3" => { + mod solid; + pub use self::solid::*; + } + target_os = "hermit" => { + mod hermit; + pub use self::hermit::*; + } + target_os = "motor" => { + mod motor; + pub use self::motor::*; + } + target_os = "trusty" => { + mod trusty; + pub use self::trusty::*; + } + target_os = "vexos" => { + mod vexos; + pub use self::vexos::*; + } + target_os = "wasi" => { + mod wasi; + pub use self::wasi::*; + } + target_family = "wasm" => { + mod wasm; + pub use self::wasm::*; + } + target_os = "xous" => { + mod xous; + pub use self::xous::*; + } + target_os = "uefi" => { + mod uefi; + pub use self::uefi::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use self::sgx::*; + } + target_os = "teeos" => { + mod teeos; + pub use self::teeos::*; + } + target_os = "zkvm" => { + mod zkvm; + pub use self::zkvm::*; + } + _ => { + mod unsupported; + pub use self::unsupported::*; + } +} diff --git a/crates/std/src/sys/pal/unsupported/common.rs b/crates/std/src/sys/pal/unsupported/common.rs new file mode 100644 index 0000000..d94b901 --- /dev/null +++ b/crates/std/src/sys/pal/unsupported/common.rs @@ -0,0 +1,21 @@ +use crate::io as std_io; + +// SAFETY: must be called only once during runtime initialization. +// NOTE: this is not guaranteed to run, for example when Rust code is called externally. +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +pub fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> std_io::Error { + std_io::Error::UNSUPPORTED_PLATFORM +} + +pub fn abort_internal() -> ! { + core::intrinsics::abort(); +} diff --git a/crates/std/src/sys/pal/unsupported/mod.rs b/crates/std/src/sys/pal/unsupported/mod.rs new file mode 100644 index 0000000..0f15781 --- /dev/null +++ b/crates/std/src/sys/pal/unsupported/mod.rs @@ -0,0 +1,6 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +pub mod os; + +mod common; +pub use common::*; diff --git a/crates/std/src/sys/pal/unsupported/os.rs b/crates/std/src/sys/pal/unsupported/os.rs new file mode 100644 index 0000000..9956845 --- /dev/null +++ b/crates/std/src/sys/pal/unsupported/os.rs @@ -0,0 +1,61 @@ +use super::unsupported; +use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; +use crate::path::{self, PathBuf}; +use crate::{fmt, io}; + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl crate::error::Error for JoinPathsError {} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on this platform") +} + +pub fn home_dir() -> Option { + None +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/crates/std/src/sys/path/unix.rs b/crates/std/src/sys/path/unix.rs index 0ee99c6..611d250 100644 --- a/crates/std/src/sys/path/unix.rs +++ b/crates/std/src/sys/path/unix.rs @@ -45,8 +45,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { PathBuf::new() } } else { - // env::current_dir()? - todo!() + env::current_dir()? }; normalized.extend(components); diff --git a/crates/std/src/sys/process/env.rs b/crates/std/src/sys/process/env.rs new file mode 100644 index 0000000..e08b476 --- /dev/null +++ b/crates/std/src/sys/process/env.rs @@ -0,0 +1,115 @@ +use crate::collections::BTreeMap; +use crate::ffi::{OsStr, OsString}; +use crate::sys::process::EnvKey; +use crate::{env, fmt}; + +/// Stores a set of changes to an environment +#[derive(Clone, Default)] +pub struct CommandEnv { + clear: bool, + saw_path: bool, + vars: BTreeMap>, +} + +impl fmt::Debug for CommandEnv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug_command_env = f.debug_struct("CommandEnv"); + debug_command_env.field("clear", &self.clear).field("vars", &self.vars); + debug_command_env.finish() + } +} + +impl CommandEnv { + // Capture the current environment with these changes applied + pub fn capture(&self) -> BTreeMap { + let mut result = BTreeMap::::new(); + if !self.clear { + for (k, v) in env::vars_os() { + result.insert(k.into(), v); + } + } + for (k, maybe_v) in &self.vars { + if let &Some(ref v) = maybe_v { + result.insert(k.clone(), v.clone()); + } else { + result.remove(k); + } + } + result + } + + pub fn is_unchanged(&self) -> bool { + !self.clear && self.vars.is_empty() + } + + pub fn capture_if_changed(&self) -> Option> { + if self.is_unchanged() { None } else { Some(self.capture()) } + } + + // The following functions build up changes + pub fn set(&mut self, key: &OsStr, value: &OsStr) { + let key = EnvKey::from(key); + self.maybe_saw_path(&key); + self.vars.insert(key, Some(value.to_owned())); + } + + pub fn remove(&mut self, key: &OsStr) { + let key = EnvKey::from(key); + self.maybe_saw_path(&key); + if self.clear { + self.vars.remove(&key); + } else { + self.vars.insert(key, None); + } + } + + pub fn clear(&mut self) { + self.clear = true; + self.vars.clear(); + } + + pub fn does_clear(&self) -> bool { + self.clear + } + + pub fn have_changed_path(&self) -> bool { + self.saw_path || self.clear + } + + fn maybe_saw_path(&mut self, key: &EnvKey) { + if !self.saw_path && key == "PATH" { + self.saw_path = true; + } + } + + pub fn iter(&self) -> CommandEnvs<'_> { + let iter = self.vars.iter(); + CommandEnvs { iter } + } +} + +#[derive(Debug)] +pub struct CommandEnvs<'a> { + iter: crate::collections::btree_map::Iter<'a, EnvKey, Option>, +} + +impl<'a> Iterator for CommandEnvs<'a> { + type Item = (&'a OsStr, Option<&'a OsStr>); + + fn next(&mut self) -> Option { + self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref())) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandEnvs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} diff --git a/crates/std/src/sys/process/mod.rs b/crates/std/src/sys/process/mod.rs new file mode 100644 index 0000000..121d3bc --- /dev/null +++ b/crates/std/src/sys/process/mod.rs @@ -0,0 +1,86 @@ +cfg_select! { + target_family = "unix" => { + mod unix; + use unix as imp; + } + target_os = "windows" => { + mod windows; + use windows as imp; + } + target_os = "uefi" => { + mod uefi; + use uefi as imp; + } + target_os = "motor" => { + mod motor; + use motor as imp; + } + _ => { + mod unsupported; + use unsupported as imp; + } +} + +// This module is shared by all platforms, but nearly all platforms except for +// the "normal" UNIX ones leave some of this code unused. +#[cfg_attr(not(target_os = "linux"), allow(dead_code))] +mod env; + +pub use env::CommandEnvs; +pub use imp::{ + ChildPipe, Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, + read_output, +}; + +#[cfg(any( + all( + target_family = "unix", + not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )) + ), + target_os = "windows", + target_os = "motor" +))] +pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec)> { + let (mut process, mut pipes) = cmd.spawn(Stdio::MakePipe, false)?; + + drop(pipes.stdin.take()); + let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); + match (pipes.stdout.take(), pipes.stderr.take()) { + (None, None) => {} + (Some(out), None) => { + let res = out.read_to_end(&mut stdout); + res.unwrap(); + } + (None, Some(err)) => { + let res = err.read_to_end(&mut stderr); + res.unwrap(); + } + (Some(out), Some(err)) => { + let res = read_output(out, &mut stdout, err, &mut stderr); + res.unwrap(); + } + } + + let status = process.wait()?; + Ok((status, stdout, stderr)) +} + +#[cfg(not(any( + all( + target_family = "unix", + not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )) + ), + target_os = "windows", + target_os = "motor" +)))] +pub use imp::output; diff --git a/crates/std/src/sys/process/unsupported.rs b/crates/std/src/sys/process/unsupported.rs new file mode 100644 index 0000000..a279d1c --- /dev/null +++ b/crates/std/src/sys/process/unsupported.rs @@ -0,0 +1,331 @@ +use super::env::{CommandEnv, CommandEnvs}; +pub use crate::ffi::OsString as EnvKey; +use crate::ffi::{OsStr, OsString}; +use crate::num::NonZero; +use crate::path::Path; +use crate::process::StdioPipes; +// use crate::sys::fs::File; +use crate::sys::unsupported; +use crate::{fmt, io}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + program: OsString, + args: Vec, + env: CommandEnv, + + cwd: Option, + stdin: Option, + stdout: Option, + stderr: Option, +} + +#[derive(Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, + ParentStdout, + ParentStderr, + // #[allow(dead_code)] // This variant exists only for the Debug impl + // InheritFile(File), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_owned(), + args: vec![program.to_owned()], + env: Default::default(), + cwd: None, + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_owned()); + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_owned()); + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn get_program(&self) -> &OsStr { + &self.program + } + + pub fn get_args(&self) -> CommandArgs<'_> { + let mut iter = self.args.iter(); + iter.next(); + CommandArgs { iter } + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.env.iter() + } + + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + + pub fn get_current_dir(&self) -> Option<&Path> { + self.cwd.as_ref().map(|cs| Path::new(cs)) + } + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +pub fn output(_cmd: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> { + unsupported() +} + +impl From for Stdio { + fn from(pipe: ChildPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + Stdio::ParentStdout + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::ParentStderr + } +} + +// impl From for Stdio { +// fn from(file: File) -> Stdio { +// Stdio::InheritFile(file) +// } +// } + +impl fmt::Debug for Command { + // show all attributes + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut debug_command = f.debug_struct("Command"); + debug_command + .field("program", &self.program) + .field("args", &self.args); + if !self.env.is_unchanged() { + debug_command.field("env", &self.env); + } + + if self.cwd.is_some() { + debug_command.field("cwd", &self.cwd); + } + + if self.stdin.is_some() { + debug_command.field("stdin", &self.stdin); + } + if self.stdout.is_some() { + debug_command.field("stdout", &self.stdout); + } + if self.stderr.is_some() { + debug_command.field("stderr", &self.stderr); + } + + debug_command.finish() + } else { + if let Some(ref cwd) = self.cwd { + write!(f, "cd {cwd:?} && ")?; + } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. + for (key, value_opt) in self.get_envs() { + if let Some(value) = value_opt { + write!(f, "{}={value:?} ", key.to_string_lossy())?; + } + } + if self.program != self.args[0] { + write!(f, "[{:?}] ", self.program)?; + } + write!(f, "{:?}", self.args[0])?; + + for arg in &self.args[1..] { + write!(f, " {:?}", arg)?; + } + Ok(()) + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +#[non_exhaustive] +pub struct ExitStatus(); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + Ok(()) + } + + pub fn code(&self) -> Option { + Some(0) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "") + } +} + +pub struct ExitStatusError(!); + +impl Clone for ExitStatusError { + fn clone(&self) -> ExitStatusError { + self.0 + } +} + +impl Copy for ExitStatusError {} + +impl PartialEq for ExitStatusError { + fn eq(&self, _other: &ExitStatusError) -> bool { + self.0 + } +} + +impl Eq for ExitStatusError {} + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + self.0 + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + self.0 + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(u8); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(0); + pub const FAILURE: ExitCode = ExitCode(1); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + Self(code) + } +} + +pub struct Process(!); + +impl Process { + pub fn id(&self) -> u32 { + self.0 + } + + pub fn kill(&mut self) -> io::Result<()> { + self.0 + } + + pub fn wait(&mut self) -> io::Result { + self.0 + } + + pub fn try_wait(&mut self) -> io::Result> { + self.0 + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, OsString>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|os| &**os) + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + _stdout: &mut Vec, + _err: ChildPipe, + _stderr: &mut Vec, +) -> io::Result<()> { + match out.diverge() {} +} diff --git a/crates/std/src/sys/thread_local/mod.rs b/crates/std/src/sys/thread_local/mod.rs index 8a44ee4..a9f1d84 100644 --- a/crates/std/src/sys/thread_local/mod.rs +++ b/crates/std/src/sys/thread_local/mod.rs @@ -128,9 +128,9 @@ pub(crate) mod guard { mod solid; pub(crate) use solid::enable; } - target_os = "survos" => { - // todo - pub(crate) fn enable() {} + target_os = "survos" => { + // todo + pub(crate) fn enable() {} } _ => { mod key; diff --git a/justfile b/justfile index 48bf23d..87c9cb8 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,6 @@ release := "" qemu_flags := "" cargo_flags := "" + if release != "" { "--release" } else { "" } bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" } -rust_src := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src" default: run @@ -13,143 +12,8 @@ mount_filesystem: sync_filesystem: sync -cp_std path: - @echo "Copying {{ path }}" - @mkdir {{ "crates/std/src" / parent_directory(path) }} -p - @cp {{ rust_src / path }} {{ "crates/std/src" / path }} - @sed -i -f patches.sed {{ "crates/std/src" / path }} - -insert_target line path: - @sed -i '{{ line }}a \target_os = "survos",' {{ "crates/std/src" / path }} - -update_std: - @# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs - # @just cp_std "process.rs" - # @just cp_std "fs.rs" - @just cp_std "time.rs" - @just cp_std "bstr.rs" - @just cp_std "panicking.rs" - @just cp_std "panic.rs" - @just cp_std "collections/hash/map.rs" - @just cp_std "collections/hash/set.rs" - @just cp_std "collections/hash/mod.rs" - @just cp_std "collections/mod.rs" - @just cp_std "io/error.rs" - @just cp_std "io/error/repr_bitpacked.rs" - @just cp_std "io/error/repr_unpacked.rs" - @just cp_std "io/error/tests.rs" - @just cp_std "io/cursor.rs" - @just cp_std "io/cursor/tests.rs" - @just cp_std "io/prelude.rs" - @just cp_std "io/impls.rs" - @just cp_std "io/impls/tests.rs" - @just cp_std "io/tests.rs" - @just cp_std "io/util.rs" - @just cp_std "io/util/tests.rs" - @just cp_std "io/copy.rs" - @just cp_std "io/copy/tests.rs" - @just cp_std "io/pipe.rs" - @just cp_std "io/pipe/tests.rs" - # @just cp_std "io/stdio.rs" - @just cp_std "io/buffered/mod.rs" - @just cp_std "io/buffered/bufreader.rs" - @just cp_std "io/buffered/bufreader/buffer.rs" - @just cp_std "io/buffered/bufwriter.rs" - @just cp_std "io/buffered/linewriter.rs" - @just cp_std "io/buffered/linewritershim.rs" - @just cp_std "sync/once.rs" - @just cp_std "sync/once_lock.rs" - @just cp_std "sync/lazy_lock.rs" - @just cp_std "sync/nonpoison.rs" - @just cp_std "sync/nonpoison/condvar.rs" - @just cp_std "sync/nonpoison/mutex.rs" - @just cp_std "sync/nonpoison/rwlock.rs" - @just cp_std "sync/poison.rs" - @just cp_std "sync/poison/condvar.rs" - @just cp_std "sync/poison/mutex.rs" - @just cp_std "sync/poison/rwlock.rs" - @just cp_std "sync/barrier.rs" - @just cp_std "sync/reentrant_lock.rs" - @just cp_std "sync/mpsc.rs" - @just cp_std "sync/mpmc/mod.rs" - @just cp_std "sync/mpmc/array.rs" - @just cp_std "sync/mpmc/context.rs" - @just cp_std "sync/mpmc/counter.rs" - @just cp_std "sync/mpmc/error.rs" - @just cp_std "sync/mpmc/list.rs" - @just cp_std "sync/mpmc/select.rs" - @just cp_std "sync/mpmc/tests.rs" - @just cp_std "sync/mpmc/utils.rs" - @just cp_std "sync/mpmc/waker.rs" - @just cp_std "sync/mpmc/zero.rs" - @just cp_std "hash/mod.rs" - @just cp_std "hash/random.rs" - @just cp_std "num/mod.rs" - @just cp_std "ffi/c_str.rs" - @just cp_std "ffi/mod.rs" - @just cp_std "ffi/os_str.rs" - @just cp_std "ffi/os_str/tests.rs" - @just cp_std "thread/local.rs" - @just cp_std "thread/thread.rs" - @just cp_std "thread/id.rs" - @just cp_std "thread/main_thread.rs" - @just cp_std "thread/current.rs" - @just cp_std "thread/join_handle.rs" - @just cp_std "thread/functions.rs" - @just cp_std "thread/lifecycle.rs" - @just cp_std "thread/builder.rs" - @just cp_std "thread/scoped.rs" - @just cp_std "thread/spawnhook.rs" - @just cp_std "sys/exit.rs" - @just cp_std "sys/env_consts.rs" - @just cp_std "sys/configure_builtins.rs" - @just cp_std "sys/cmath.rs" - @just cp_std "sys/sync/condvar/mod.rs" - @just cp_std "sys/sync/condvar/no_threads.rs" - @just cp_std "sys/sync/mutex/mod.rs" - @just cp_std "sys/sync/mutex/no_threads.rs" - @just cp_std "sys/sync/once/mod.rs" - @just cp_std "sys/sync/once/no_threads.rs" - @just cp_std "sys/sync/rwlock/mod.rs" - @just cp_std "sys/sync/rwlock/no_threads.rs" - @just cp_std "sys/sync/thread_parking/mod.rs" - @just cp_std "sys/sync/thread_parking/unsupported.rs" - @just cp_std "sys/thread/mod.rs" - @just cp_std "sys/thread/unsupported.rs" - @just cp_std "sys/thread_local/no_threads.rs" - @just cp_std "sys/thread_local/os.rs" - @just cp_std "sys/time/mod.rs" - @just cp_std "sys/time/unsupported.rs" - @just cp_std "sys/random/mod.rs" - @just cp_std "sys/random/unsupported.rs" - @just cp_std "sys/env/mod.rs" - @just cp_std "sys/env/common.rs" - @just cp_std "sys/env/unsupported.rs" - @just cp_std "sys/os_str/mod.rs" - @just cp_std "sys/os_str/bytes.rs" - @just cp_std "sys/os_str/bytes/tests.rs" - @just cp_std "sys/path/mod.rs" - @just cp_std "sys/fs/mod.rs" - @just cp_std "sys/fs/common.rs" - @just cp_std "sys/fs/unsupported.rs" - @just cp_std "sys/io/error/generic.rs" - @just cp_std "sys/io/io_slice/unsupported.rs" - @just cp_std "sys/io/is_terminal/unsupported.rs" - @just cp_std "sys/io/kernel_copy/mod.rs" - @just cp_std "sys/io/mod.rs" - @just cp_std "sys/pipe/mod.rs" - @just cp_std "sys/pipe/unsupported.rs" - @just cp_std "sys/stdio/mod.rs" - @just cp_std "sys/stdio/unsupported.rs" - @just insert_target 108 "sys/random/mod.rs" - @just insert_target 125 "sys/random/mod.rs" - @# Copied but edited for the moment - # @just cp_std "sys/thread_local/mod.rs" - # @just cp_std "sys/alloc/mod.rs" - # @just cp_std "sys/io/error/mod.rs" - # @just cp_std "alloc.rs" - # @just cp_std "path.rs" - # @just cp_std "sys/path/unix.rs" +update-std: + @cd crates/std && just update-std build_user_prog prog: RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }} diff --git a/user/shell/src/main.rs b/user/shell/src/main.rs index d8a5d7a..b9a3d2a 100644 --- a/user/shell/src/main.rs +++ b/user/shell/src/main.rs @@ -1,8 +1,11 @@ #![feature(custom_std)] -fn main() -> isize { +fn main() { + let a = std::env::args(); + for a in a { + println!("Argument: {}", a); + } println!( "Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! " ); - 0 } diff --git a/user/test_pic/src/main.rs b/user/test_pic/src/main.rs index 11fca05..65f471e 100644 --- a/user/test_pic/src/main.rs +++ b/user/test_pic/src/main.rs @@ -2,7 +2,7 @@ #![allow(unused)] use io::{Read, Write}; -use std::{io::stdin, syscall}; +use std::{io::{Stdin, stdin}, syscall}; fn main() { // let mut input = String::new(); @@ -15,8 +15,8 @@ fn main() { syscall::spawn("/usr/bin/shell"); loop { let mut test = [0; 2]; - let len = stdin().read(&mut test).unwrap(); + let len = tty.read(&mut test).unwrap(); tty.write(str::from_utf8(&test[..len as usize]).unwrap().as_bytes()) - .unwrap(); + .unwrap(); } }