Skip to content

Instantly share code, notes, and snippets.

@krobelus
Created January 7, 2024 09:32
Show Gist options
  • Save krobelus/72da136712c3016561ed521198e79c33 to your computer and use it in GitHub Desktop.
Save krobelus/72da136712c3016561ed521198e79c33 to your computer and use it in GitHub Desktop.
prr
> diff --git a/Cargo.toml b/Cargo.toml
> index 1ca5a435ad0c..18b289c4a2cb 100644
> --- a/Cargo.toml
> +++ b/Cargo.toml
> @@ -45,7 +45,7 @@ printf-compat = { git = "https://github.com/fish-shell/printf-compat.git", branc
> autocxx = "0.23.1"
> bitflags = "2.4.0"
> cxx = "1.0"
> -errno = "0.2.8"
> +_errno = { package="errno", version = "0.2.8" }
> inventory = { version = "0.3.3", optional = true}
> lazy_static = "1.4.0"
> libc = "0.2.137"
> diff --git a/fish-rust/src/builtins/cd.rs b/fish-rust/src/builtins/cd.rs
> index 982adb348377..0d754776db2e 100644
> --- a/fish-rust/src/builtins/cd.rs
> +++ b/fish-rust/src/builtins/cd.rs
> @@ -5,10 +5,11 @@ use crate::{
> env::{EnvMode, Environment},
> fds::{wopen_cloexec, AutoCloseFd},
> path::path_apply_cdpath,
> + set_errno,
> wutil::{normalize_path, wperror, wreadlink},
> };
> -use errno::{self, Errno};
> -use libc::{fchdir, EACCES, ELOOP, ENOENT, ENOTDIR, EPERM, O_RDONLY};
> +use libc::{fchdir, O_RDONLY};
> +use nix::errno::Errno;
> use std::sync::Arc;
>
> // The cd builtin. Changes the current directory to the one specified or to $HOME if none is
> @@ -77,14 +78,14 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Optio
> return STATUS_CMD_ERROR;
> }
>
> - let mut best_errno = 0;
> + let mut best_errno = Errno::from_i32(0);
> let mut broken_symlink = WString::new();
> let mut broken_symlink_target = WString::new();
>
> for dir in dirs {
> let norm_dir = normalize_path(&dir, true);
>
> - errno::set_errno(Errno(0));
> + Errno::clear();
>
> // We need to keep around the fd for this directory, in the parser.
> let dir_fd = Arc::new(AutoCloseFd::new(wopen_cloexec(&norm_dir, O_RDONLY, 0)));
> @@ -94,22 +95,27 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Optio
> // ENOENT in particular is very low priority
> // - if in another directory there was a *file* by the correct name
> // we prefer *that* error because it's more specific
> - if errno::errno().0 == ENOENT {
> - let tmp = wreadlink(&norm_dir);
> - // clippy doesn't like this is_some/unwrap pair, but using if let is harder to read IMO
> - #[allow(clippy::unnecessary_unwrap)]
> - if broken_symlink.is_empty() && tmp.is_some() {
> - broken_symlink = norm_dir;
> - broken_symlink_target = tmp.unwrap();
> - } else if best_errno == 0 {
> - best_errno = errno::errno().0;
> + match Errno::last() {
> + Errno::ENOENT => {
> + let tmp = wreadlink(&norm_dir);
> + // clippy doesn't like this is_some/unwrap pair, but using if let is harder to read IMO
> + #[allow(clippy::unnecessary_unwrap)]
> + if broken_symlink.is_empty() && tmp.is_some() {
> + broken_symlink = norm_dir;
> + broken_symlink_target = tmp.unwrap();
> + } else if best_errno == Errno::from_i32(0) {
> + best_errno = Errno::last();
> + }
> + continue;
> }
> - continue;
> - } else if errno::errno().0 == ENOTDIR {
> - best_errno = errno::errno().0;
> - continue;
> + Errno::ENOTDIR => {
> + best_errno = Errno::ENOTDIR;
> + continue;
> + }
> + _ => {}
> }
> - best_errno = errno::errno().0;
> +
> + best_errno = Errno::last();
> break;
> }
>
> @@ -120,45 +126,52 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Optio
> return STATUS_CMD_OK;
> }
>
> - if best_errno == ENOTDIR {
> - streams.err.append(wgettext_fmt!(
> - "%ls: '%ls' is not a directory\n",
> - cmd,
> - dir_in
> - ));
> - } else if !broken_symlink.is_empty() {
> - streams.err.append(wgettext_fmt!(
> - "%ls: '%ls' is a broken symbolic link to '%ls'\n",
> - cmd,
> - broken_symlink,
> - broken_symlink_target
> - ));
> - } else if best_errno == ELOOP {
> - streams.err.append(wgettext_fmt!(
> - "%ls: Too many levels of symbolic links: '%ls'\n",
> - cmd,
> - dir_in
> - ));
> - } else if best_errno == ENOENT {
> - streams.err.append(wgettext_fmt!(
> - "%ls: The directory '%ls' does not exist\n",
> - cmd,
> - dir_in
> - ));
> - } else if best_errno == EACCES || best_errno == EPERM {
> - streams.err.append(wgettext_fmt!(
> - "%ls: Permission denied: '%ls'\n",
> - cmd,
> - dir_in
> - ));
> - } else {
> - errno::set_errno(Errno(best_errno));
> - wperror(L!("cd"));
> - streams.err.append(wgettext_fmt!(
> - "%ls: Unknown error trying to locate directory '%ls'\n",
> - cmd,
> - dir_in
> - ));
> + match best_errno {
> + Errno::ENOTDIR => {
> + streams.err.append(wgettext_fmt!(
> + "%ls: '%ls' is not a directory\n",
> + cmd,
> + dir_in
> + ));
> + }
> + _ if !broken_symlink.is_empty() => {
> + streams.err.append(wgettext_fmt!(
> + "%ls: '%ls' is a broken symbolic link to '%ls'\n",
> + cmd,
> + broken_symlink,
> + broken_symlink_target
> + ));
> + }
> + Errno::ELOOP => {
> + streams.err.append(wgettext_fmt!(
> + "%ls: Too many levels of symbolic links: '%ls'\n",
> + cmd,
> + dir_in
> + ));
> + }
> + Errno::ENOENT => {
> + streams.err.append(wgettext_fmt!(
> + "%ls: The directory '%ls' does not exist\n",
> + cmd,
> + dir_in
> + ));
> + }
> + Errno::EACCES | Errno::EPERM => {
> + streams.err.append(wgettext_fmt!(
> + "%ls: Permission denied: '%ls'\n",
> + cmd,
> + dir_in
> + ));
> + }
> + _ => {
> + set_errno(best_errno);
> + wperror(L!("cd"));
> + streams.err.append(wgettext_fmt!(
> + "%ls: Unknown error trying to locate directory '%ls'\n",
> + cmd,
> + dir_in
> + ));
> + }
> }
>
> if !parser.is_interactive() {
> diff --git a/fish-rust/src/builtins/pwd.rs b/fish-rust/src/builtins/pwd.rs
> index 70243fbb8cb2..e6f212a406c7 100644
> --- a/fish-rust/src/builtins/pwd.rs
> +++ b/fish-rust/src/builtins/pwd.rs
> @@ -1,5 +1,5 @@
> //! Implementation of the pwd builtin.
> -use errno::errno;
> +use nix::errno::Errno;
>
> use super::prelude::*;
> use crate::{env::Environment, wutil::wrealpath};
> @@ -51,7 +51,7 @@ pub fn pwd(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opti
> streams.err.append(wgettext_fmt!(
> "%ls: realpath failed: %s\n",
> cmd,
> - errno().to_string()
> + Errno::last().desc()
> ));
> return STATUS_CMD_ERROR;
> }
> diff --git a/fish-rust/src/builtins/realpath.rs b/fish-rust/src/builtins/realpath.rs
> index 5605ae9fca54..b7402ba968be 100644
> --- a/fish-rust/src/builtins/realpath.rs
> +++ b/fish-rust/src/builtins/realpath.rs
> @@ -1,6 +1,6 @@
> //! Implementation of the realpath builtin.
>
> -use errno::errno;
> +use nix::errno::Errno;
>
> use super::prelude::*;
> use crate::env::Environment;
> @@ -86,15 +86,15 @@ pub fn realpath(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
> if let Some(real_path) = wrealpath(arg) {
> streams.out.append(real_path);
> } else {
> - let errno = errno();
> - if errno.0 != 0 {
> + let errno = Errno::last();
> + if errno != Errno::from_i32(0) {
> // realpath() just couldn't do it. Report the error and make it clear
> // this is an error from our builtin, not the system's realpath.
> streams.err.append(wgettext_fmt!(
> "builtin %ls: %ls: %s\n",
> cmd,
> arg,
> - errno.to_string()
> + errno.desc()
> ));
> } else {
> // Who knows. Probably a bug in our wrealpath() implementation.
> @@ -120,7 +120,7 @@ pub fn realpath(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
> streams.err.append(wgettext_fmt!(
> "builtin %ls: realpath failed: %s\n",
> cmd,
> - errno().to_string()
> + Errno::last().desc()
> ));
> return STATUS_CMD_ERROR;
> }
> diff --git a/fish-rust/src/builtins/shared.rs b/fish-rust/src/builtins/shared.rs
> index 8f2119028eaa..ab2c4aef0503 100644
> --- a/fish-rust/src/builtins/shared.rs
> +++ b/fish-rust/src/builtins/shared.rs
> @@ -11,8 +11,8 @@ use crate::reader::reader_read;
> use crate::wchar::{wstr, WString, L};
> use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t};
> use cxx::CxxWString;
> -use errno::errno;
> use libc::{c_int, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
> +use nix::errno::Errno;
>
> use std::borrow::Cow;
> use std::fs::File;
> @@ -641,11 +641,11 @@ pub fn builtin_print_error_trailer(parser: &Parser, b: &mut OutputStream, cmd: &
> /// This function works like perror, but it prints its result into the streams.err string instead
> /// to stderr. Used by the builtin commands.
> pub fn builtin_wperror(program_name: &wstr, streams: &mut IoStreams) {
> - let err = errno();
> + let err = Errno::last();
> streams.err.append(program_name);
> streams.err.append(L!(": "));
> - if err.0 != 0 {
> - let werr = WString::from_str(&err.to_string());
> + if err != Errno::from_i32(0) {
> + let werr = WString::from_str(&err.desc());
> streams.err.append(werr);
> streams.err.push('\n');
> }
> diff --git a/fish-rust/src/common.rs b/fish-rust/src/common.rs
> index ea771f35be0b..8b6acd6174d0 100644
> --- a/fish-rust/src/common.rs
> +++ b/fish-rust/src/common.rs
> @@ -20,7 +20,8 @@ use crate::wutil::fish_iswalnum;
> use bitflags::bitflags;
> use core::slice;
> use cxx::{CxxWString, UniquePtr};
> -use libc::{EINTR, EIO, O_WRONLY, SIGTTOU, SIG_IGN, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
> +use libc::{O_WRONLY, SIGTTOU, SIG_IGN, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
> +use nix::errno::Errno;
> use num_traits::ToPrimitive;
> use once_cell::sync::{Lazy, OnceCell};
> use std::env;
> @@ -1485,7 +1486,7 @@ fn can_be_encoded(wc: char) -> bool {
> pub fn read_blocked(fd: RawFd, buf: &mut [u8]) -> isize {
> loop {
> let res = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) };
> - if res < 0 && errno::errno().0 == EINTR {
> + if res < 0 && Errno::last() == Errno::EINTR {
> continue;
> }
> return res;
> @@ -1512,11 +1513,11 @@ pub fn write_loop<Fd: AsRawFd>(fd: &Fd, buf: &[u8]) -> std::io::Result<usize> {
> let written =
> unsafe { libc::write(fd, buf[total..].as_ptr() as *const _, buf.len() - total) };
> if written < 0 {
> - let errno = errno::errno().0;
> - if matches!(errno, libc::EAGAIN | libc::EINTR) {
> + let errno = Errno::last();
> + if matches!(errno, Errno::EAGAIN | Errno::EINTR) {
> continue;
> }
> - return Err(std::io::Error::from_raw_os_error(errno));
> + return Err(std::io::Error::from(errno));
> }
> total += written as usize;
> }
> @@ -1532,11 +1533,11 @@ pub fn read_loop<Fd: AsRawFd>(fd: &Fd, buf: &mut [u8]) -> std::io::Result<usize>
> loop {
> let read = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
> if read < 0 {
> - let errno = errno::errno().0;
> - if matches!(errno, libc::EAGAIN | libc::EINTR) {
> + let errno = Errno::last();
> + if matches!(errno, Errno::EAGAIN | Errno::EINTR) {
> continue;
> }
> - return Err(std::io::Error::from_raw_os_error(errno));
> + return Err(std::io::Error::from(errno));
> }
> return Ok(read as usize);
> }
> @@ -1751,7 +1752,7 @@ pub fn redirect_tty_output() {
> let fd = libc::open(s.as_ptr(), O_WRONLY);
> assert!(fd != -1, "Could not open /dev/null!");
> for stdfd in [STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO] {
> - if libc::tcgetattr(stdfd, &mut t) == -1 && errno::errno().0 == EIO {
> + if libc::tcgetattr(stdfd, &mut t) == -1 && Errno::last() == Errno::EIO {
> libc::dup2(fd, stdfd);
> }
> }
> diff --git a/fish-rust/src/env_universal_common.rs b/fish-rust/src/env_universal_common.rs
> index 6a797cc66582..46001e962ae0 100644
> --- a/fish-rust/src/env_universal_common.rs
> +++ b/fish-rust/src/env_universal_common.rs
> @@ -17,10 +17,8 @@ use crate::wutil::{
> file_id_for_fd, file_id_for_path, file_id_for_path_narrow, wdirname, wrealpath, wrename, wstat,
> wunlink, FileId, INVALID_FILE_ID,
> };
> -use errno::{errno, Errno};
> -use libc::{
> - CLOCK_REALTIME, EINTR, ENOTSUP, EOPNOTSUPP, LOCK_EX, O_CREAT, O_RDONLY, O_RDWR, UTIME_OMIT,
> -};
> +use libc::{CLOCK_REALTIME, LOCK_EX, O_CREAT, O_RDONLY, O_RDWR, UTIME_OMIT};
> +use nix::errno::Errno;
> use std::collections::hash_map::Entry;
> use std::collections::HashSet;
> use std::ffi::CString;
> @@ -442,13 +440,14 @@ impl EnvUniversal {
> fd = AutoCloseFd::new(wopen_cloexec(&self.vars_path, flags, 0o644));
>
> if !fd.is_valid() {
> - let err = errno();
> - if err.0 == EINTR {
> + let err = Errno::last();
> + if err == Errno::EINTR {
> continue; // signaled; try again
> }
>
> if O_EXLOCK != 0 {
> - if (flags & O_EXLOCK) != 0 && [ENOTSUP, EOPNOTSUPP].contains(&err.0) {
> + if (flags & O_EXLOCK) != 0 && [Errno::ENOTSUP, Errno::EOPNOTSUPP].contains(&err)
> + {
> // Filesystem probably does not support locking. Give up on locking.
> // Note that on Linux the two errno symbols have the same value but on BSD they're
> // different.
> @@ -496,7 +495,7 @@ impl EnvUniversal {
> // This should almost always succeed on the first try.
> assert!(!string_suffixes_string(L!("/"), directory));
>
> - let mut saved_errno = Errno(0);
> + let mut saved_errno = Errno::from_i32(0);
> let tmp_name_template = directory.to_owned() + L!("/fishd.tmp.XXXXXX");
> let mut result = AutoCloseFd::empty();
> let mut narrow_str = CString::default();
> @@ -507,7 +506,7 @@ impl EnvUniversal {
> let (fd, tmp_name) = fish_mkstemp_cloexec(wcs2zstring(&tmp_name_template));
> result.reset(fd);
> narrow_str = tmp_name;
> - saved_errno = errno();
> + saved_errno = Errno::last();
> }
> *out_path = str2wcstring(narrow_str.as_bytes());
>
> @@ -529,7 +528,7 @@ impl EnvUniversal {
> let mut success = true;
> let contents = Self::serialize_with_vars(&self.vars);
> if let Err(err) = write_loop(&fd, &contents) {
> - let error = Errno(err.raw_os_error().unwrap());
> + let error = Errno::try_from(err).unwrap();
> FLOG!(
> error,
> wgettext_fmt!(
> @@ -551,7 +550,7 @@ impl EnvUniversal {
> fn move_new_vars_file_into_place(&mut self, src: &wstr, dst: &wstr) -> bool {
> let ret = wrename(src, dst);
> if ret != 0 {
> - let error = errno();
> + let error = Errno::last();
> FLOG!(
> error,
> wgettext_fmt!(
> @@ -1017,7 +1016,7 @@ fn encode_serialized(vals: &[WString]) -> WString {
> fn flock_uvar_file(fd: RawFd) -> bool {
> let start_time = timef();
> while unsafe { libc::flock(fd, LOCK_EX) } == -1 {
> - if errno().0 != EINTR {
> + if Errno::last() != Errno::EINTR {
> return false; // do nothing per issue #2149
> }
> }
> diff --git a/fish-rust/src/exec.rs b/fish-rust/src/exec.rs
> index 475359ce68f0..e0803c84609e 100644
> --- a/fish-rust/src/exec.rs
> +++ b/fish-rust/src/exec.rs
> @@ -42,6 +42,7 @@ use crate::proc::{
> };
> use crate::reader::{reader_run_count, restore_term_mode};
> use crate::redirection::{dup2_list_resolve_chain, Dup2List};
> +use crate::set_errno;
> use crate::threads::{iothread_perform_cant_wait, is_forked_child};
> use crate::timer::push_timer;
> use crate::trace::trace_if_enabled_with_args;
> @@ -52,12 +53,12 @@ use crate::wchar_ffi::WCharToFFI;
> use crate::wutil::{fish_wcstol, perror};
> use crate::wutil::{wgettext, wgettext_fmt};
> use cxx::{CxxWString, UniquePtr};
> -use errno::{errno, set_errno};
> use libc::c_int;
> use libc::{
> - c_char, EACCES, ENOENT, ENOEXEC, ENOTDIR, EPIPE, EXIT_FAILURE, EXIT_SUCCESS, O_NOCTTY,
> - O_RDONLY, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO,
> + c_char, EPIPE, EXIT_FAILURE, EXIT_SUCCESS, O_NOCTTY, O_RDONLY, STDERR_FILENO, STDIN_FILENO,
> + STDOUT_FILENO,
> };
> +use nix::errno::Errno;
> use std::ffi::CStr;
> use std::io::{Read, Write};
> use std::os::fd::{FromRawFd, RawFd};
> @@ -311,20 +312,20 @@ static FORK_COUNT: AtomicUsize = AtomicUsize::new(0);
> type LaunchResult = Result<(), ()>;
>
> /// Given an error \p err returned from either posix_spawn or exec, \return a process exit code.
> -fn exit_code_from_exec_error(err: libc::c_int) -> libc::c_int {
> - assert!(err != 0, "Zero is success, not an error");
> +fn exit_code_from_exec_error(err: Errno) -> libc::c_int {
> + assert!(err != Errno::from_i32(0), "Zero is success, not an error");
> match err {
> - ENOENT | ENOTDIR => {
> + Errno::ENOENT | Errno::ENOTDIR => {
> // This indicates either the command was not found, or a file redirection was not found.
> // We do not use posix_spawn file redirections so this is always command-not-found.
> STATUS_CMD_UNKNOWN.unwrap()
> }
> - EACCES | ENOEXEC => {
> + Errno::EACCES | Errno::ENOEXEC => {
> // The file is not executable for various reasons.
> STATUS_NOT_EXECUTABLE.unwrap()
> }
> #[cfg(target_os = "macos")]
> - libc::EBADARCH => {
> + Errno::EBADARCH => {
> // This is for e.g. running ARM app on Intel Mac.
> STATUS_NOT_EXECUTABLE.unwrap()
> }
> @@ -370,7 +371,7 @@ pub fn is_thompson_shell_script(path: &CStr) -> bool {
> if path.to_bytes().ends_with(".fish".as_bytes()) {
> return false;
> }
> - let e = errno();
> + let e = Errno::last();
> let mut res = false;
> let fd = open_cloexec(path, O_RDONLY | O_NOCTTY, 0);
> if fd != -1 {
> @@ -398,14 +399,14 @@ fn safe_launch_process(
> // This function never returns, so we take certain liberties with constness.
>
> unsafe { libc::execve(actual_cmd.as_ptr(), argv.get(), envv.get()) };
> - let err = errno();
> + let err = Errno::last();
>
> // The shebang wasn't introduced until UNIX Seventh Edition, so if
> // the kernel won't run the binary we hand it off to the interpreter
> // after performing a binary safety check, recommended by POSIX: a
> // line needs to exist before the first \0 with a lowercase letter
>
> - if err.0 == ENOEXEC && is_thompson_shell_script(actual_cmd) {
> + if err == Errno::ENOEXEC && is_thompson_shell_script(actual_cmd) {
> // Construct new argv.
> // We must not allocate memory, so only 128 args are supported.
> const maxargs: usize = 128;
> @@ -426,8 +427,8 @@ fn safe_launch_process(
> }
>
> set_errno(err);
> - safe_report_exec_error(errno().0, actual_cmd.as_ptr(), argv.get(), envv.get());
> - exit_without_destructors(exit_code_from_exec_error(err.0));
> + safe_report_exec_error(err, actual_cmd.as_ptr(), argv.get(), envv.get());
> + exit_without_destructors(exit_code_from_exec_error(err));
> }
>
> /// This function is similar to launch_process, except it is not called after a fork (i.e. it only
> @@ -720,7 +721,7 @@ fn fork_child_for_process(
> {
> if let Some(pgid) = job.group().get_pgid() {
> let err = execute_setpgid(p.pid(), pgid, is_parent);
> - if err != 0 {
> + if let Some(err) = err {
> report_setpgid_error(
> err,
> is_parent,
> @@ -864,11 +865,9 @@ fn exec_external_command(
> let pid = match pid {
> Ok(pid) => pid,
> Err(err) => {
> - safe_report_exec_error(err.0, actual_cmd.as_ptr(), argv.get(), envv.get());
> + safe_report_exec_error(err, actual_cmd.as_ptr(), argv.get(), envv.get());
> p.status
> - .update(&ProcStatus::from_exit_code(exit_code_from_exec_error(
> - err.0,
> - )));
> + .update(&ProcStatus::from_exit_code(exit_code_from_exec_error(err)));
> return Err(());
> }
> };
> diff --git a/fish-rust/src/fd_monitor.rs b/fish-rust/src/fd_monitor.rs
> index 3bbb1c7e4c57..1c62b1559b0b 100644
> --- a/fish-rust/src/fd_monitor.rs
> +++ b/fish-rust/src/fd_monitor.rs
> @@ -11,8 +11,8 @@ use crate::ffi::void_ptr;
> use crate::flog::FLOG;
> use crate::threads::assert_is_background_thread;
> use crate::wutil::perror;
> -use errno::errno;
> -use libc::{self, c_void, EAGAIN, EINTR, EWOULDBLOCK};
> +use libc::{self, c_void};
> +use nix::errno::Errno;
>
> #[cfg(not(HAVE_EVENTFD))]
> use crate::fds::{make_autoclose_pipes, make_fd_nonblocking};
> @@ -148,11 +148,11 @@ impl FdEventSignaller {
> std::mem::size_of_val(&buff),
> )
> };
> - if ret >= 0 || errno().0 != EINTR {
> + if ret >= 0 || Errno::last() != Errno::EINTR {
> break;
> }
> }
> - if ret < 0 && ![EAGAIN, EWOULDBLOCK].contains(&errno().0) {
> + if ret < 0 && ![Errno::EAGAIN, Errno::EWOULDBLOCK].contains(&Errno::last()) {
> perror("read");
> }
> ret > 0
> @@ -175,12 +175,12 @@ impl FdEventSignaller {
> std::mem::size_of_val(&c),
> )
> };
> - if ret >= 0 || errno().0 != EINTR {
> + if ret >= 0 || Errno::last() != Errno::EINTR {
> break;
> }
> }
> // EAGAIN occurs if either the pipe buffer is full or the eventfd overflows (very unlikely).
> - if ret < 0 && ![EAGAIN, EWOULDBLOCK].contains(&errno().0) {
> + if ret < 0 && ![Errno::EAGAIN, Errno::EWOULDBLOCK].contains(&Errno::last()) {
> perror("write");
> }
> }
> @@ -640,7 +640,7 @@ impl BackgroundFdMonitor {
> .map(|duration| duration.as_micros() as u64)
> .unwrap_or(FdReadableSet::kNoTimeout),
> );
> - if ret < 0 && errno::errno().0 != libc::EINTR {
> + if ret < 0 && Errno::last() != Errno::EINTR {
> // Surprising error
> perror("select");
> }
> diff --git a/fish-rust/src/fds.rs b/fish-rust/src/fds.rs
> index 9f9981e5e631..d793ee45cbd3 100644
> --- a/fish-rust/src/fds.rs
> +++ b/fish-rust/src/fds.rs
> @@ -3,9 +3,9 @@ use crate::flog::FLOG;
> use crate::wchar::prelude::*;
> use crate::wutil::perror;
> use libc::{
> - c_int, EINTR, FD_CLOEXEC, F_DUPFD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL, O_CLOEXEC,
> - O_NONBLOCK,
> + c_int, FD_CLOEXEC, F_DUPFD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL, O_CLOEXEC, O_NONBLOCK,
> };
> +use nix::errno::Errno;
> use nix::unistd;
> use std::ffi::CStr;
> use std::io::{self, Read, Write};
> @@ -31,23 +31,13 @@ pub struct AutoCloseFd {
>
> impl Read for AutoCloseFd {
> fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
> - unsafe {
> - match libc::read(self.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len()) {
> - -1 => Err(std::io::Error::from_raw_os_error(errno::errno().0)),
> - bytes => Ok(bytes as usize),
> - }
> - }
> + unistd::read(self.as_raw_fd(), buf).map_err(std::io::Error::from)
> }
> }
>
> impl Write for AutoCloseFd {
> fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
> - unsafe {
> - match libc::write(self.as_raw_fd(), buf.as_ptr() as *const _, buf.len()) {
> - -1 => Err(std::io::Error::from_raw_os_error(errno::errno().0)),
> - bytes => Ok(bytes as usize),
> - }
> - }
> + unistd::write(self.as_raw_fd(), buf).map_err(std::io::Error::from)
> }
>
> fn flush(&mut self) -> std::io::Result<()> {
> @@ -258,8 +248,9 @@ pub fn open_cloexec(path: &CStr, flags: i32, mode: libc::c_int) -> RawFd {
> /// Close a file descriptor \p fd, retrying on EINTR.
> pub fn exec_close(fd: RawFd) {
> assert!(fd >= 0, "Invalid fd");
> - while unsafe { libc::close(fd) } == -1 {
> - if errno::errno().0 != EINTR {
> +
> + while let Err(err) = unistd::close(fd) {
> + if err != Errno::EINTR {
> perror("close");
> break;
> }
> @@ -271,10 +262,7 @@ pub fn make_fd_nonblocking(fd: RawFd) -> Result<(), io::Error> {
> let flags = unsafe { libc::fcntl(fd, F_GETFL, 0) };
> let nonblocking = (flags & O_NONBLOCK) == O_NONBLOCK;
> if !nonblocking {
> - match unsafe { libc::fcntl(fd, F_SETFL, flags | O_NONBLOCK) } {
> - -1 => return Err(io::Error::last_os_error()),
> - _ => return Ok(()),
> - };
> + Errno::result(unsafe { libc::fcntl(fd, F_SETFL, flags | O_NONBLOCK) })?;
> }
> Ok(())
> }
> @@ -284,10 +272,7 @@ pub fn make_fd_blocking(fd: RawFd) -> Result<(), io::Error> {
> let flags = unsafe { libc::fcntl(fd, F_GETFL, 0) };
> let nonblocking = (flags & O_NONBLOCK) == O_NONBLOCK;
> if nonblocking {
> - match unsafe { libc::fcntl(fd, F_SETFL, flags & !O_NONBLOCK) } {
> - -1 => return Err(io::Error::last_os_error()),
> - _ => return Ok(()),
> - };
> + Errno::result(unsafe { libc::fcntl(fd, F_SETFL, flags & !O_NONBLOCK) })?;
> }
> Ok(())
> }
> diff --git a/fish-rust/src/fork_exec/postfork.rs b/fish-rust/src/fork_exec/postfork.rs
> index 54a4190c03ca..dd88c62102a3 100644
> --- a/fish-rust/src/fork_exec/postfork.rs
> +++ b/fish-rust/src/fork_exec/postfork.rs
> @@ -7,6 +7,7 @@ use crate::nix::getpid;
> use crate::redirection::Dup2List;
> use crate::signal::signal_reset_handlers;
> use libc::{c_char, c_int, pid_t};
> +use nix::errno::Errno;
> use std::ffi::CStr;
>
> /// The number of times to try to call fork() before giving up.
> @@ -47,7 +48,7 @@ fn strlen_safe(s: *const libc::c_char) -> usize {
>
> /// Report the error code for a failed setpgid call.
> pub fn report_setpgid_error(
> - err: i32,
> + err: Errno,
> is_parent: bool,
> pid: pid_t,
> desired_pgid: pid_t,
> @@ -82,9 +83,9 @@ pub fn report_setpgid_error(
> );
>
> match err {
> - libc::EACCES => FLOG_SAFE!(error, "setpgid: Process ", pid, " has already exec'd"),
> - libc::EINVAL => FLOG_SAFE!(error, "setpgid: pgid ", cur_group, " unsupported"),
> - libc::EPERM => {
> + Errno::EACCES => FLOG_SAFE!(error, "setpgid: Process ", pid, " has already exec'd"),
> + Errno::EINVAL => FLOG_SAFE!(error, "setpgid: pgid ", cur_group, " unsupported"),
> + Errno::EPERM => {
> FLOG_SAFE!(
> error,
> "setpgid: Process ",
> @@ -94,30 +95,30 @@ pub fn report_setpgid_error(
> " does not match"
> );
> }
> - libc::ESRCH => FLOG_SAFE!(error, "setpgid: Process ID ", pid, " does not match"),
> - _ => FLOG_SAFE!(error, "setpgid: Unknown error number ", err),
> + Errno::ESRCH => FLOG_SAFE!(error, "setpgid: Process ID ", pid, " does not match"),
> + _ => FLOG_SAFE!(error, "setpgid: Unknown error number ", err as i32),
> }
> }
>
> /// Execute setpgid, moving pid into the given pgroup.
> /// Return the result of setpgid.
> -pub fn execute_setpgid(pid: pid_t, pgroup: pid_t, is_parent: bool) -> i32 {
> +pub fn execute_setpgid(pid: pid_t, pgroup: pid_t, is_parent: bool) -> Option<Errno> {
> // There is a comment "Historically we have looped here to support WSL."
> // TODO: stop looping.
> let mut eperm_count = 0;
> loop {
> if unsafe { libc::setpgid(pid, pgroup) } == 0 {
> - return 0;
> + return None;
> }
> - let err = errno::errno().0;
> - if err == libc::EACCES && is_parent {
> + let err = Errno::last();
> + if err == Errno::EACCES && is_parent {
> // We are the parent process and our child has called exec().
> // This is an unavoidable benign race.
> - return 0;
> - } else if err == libc::EINTR {
> + return None;
> + } else if err == Errno::EINTR {
> // Paranoia.
> continue;
> - } else if err == libc::EPERM && eperm_count < 100 {
> + } else if err == Errno::EPERM && eperm_count < 100 {
> eperm_count += 1;
> // The setpgid(2) man page says that EPERM is returned only if attempts are made
> // to move processes into groups across session boundaries (which can never be
> @@ -134,14 +135,14 @@ pub fn execute_setpgid(pid: pid_t, pgroup: pid_t, is_parent: bool) -> i32 {
> // and returns ESRCH (process not found) instead of EACCES (child has called exec).
> // See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=251227
> #[cfg(any(feature = "bsd", target_os = "macos"))]
> - if err == libc::ESRCH && is_parent {
> + if err == Errno::ESRCH && is_parent {
> // Handle this just like we would EACCES above, as we're virtually certain that
> // setpgid(2) was called against a process that was at least at one point in time a
> // valid child.
> - return 0;
> + return None;
> }
>
> - return err;
> + return Some(err);
> }
> }
>
> @@ -206,14 +207,14 @@ pub fn child_setup_process(
> /// FORK_LAPS times, with a very slight delay between each lap. If fork fails even then, the process
> /// will exit with an error message.
> pub fn execute_fork() -> pid_t {
> - let mut err = 0;
> + let mut err = Errno::UnknownErrno;
> for i in 0..FORK_LAPS {
> let pid = unsafe { libc::fork() };
> if pid >= 0 {
> return pid;
> }
> - err = errno::errno().0;
> - if err != libc::EAGAIN {
> + err = Errno::last();
> + if err != Errno::EAGAIN {
> break;
> }
> let pollint = libc::timespec {
> @@ -227,24 +228,24 @@ pub fn execute_fork() -> pid_t {
> }
>
> match err {
> - libc::EAGAIN => {
> + Errno::EAGAIN => {
> FLOG_SAFE!(
> error,
> "fork: Out of resources. Check RLIMIT_NPROC and pid_max."
> );
> }
> - libc::ENOMEM => {
> + Errno::ENOMEM => {
> FLOG_SAFE!(error, "fork: Out of memory.");
> }
> _ => {
> - FLOG_SAFE!(error, "fork: Unknown error number", err);
> + FLOG_SAFE!(error, "fork: Unknown error number", err as i32);
> }
> }
> exit_without_destructors(1)
> }
>
> pub fn safe_report_exec_error(
> - err: i32,
> + err: Errno,
> actual_cmd: *const c_char,
> argvv: *const *const c_char,
> envv: *const *const c_char,
> @@ -252,7 +253,7 @@ pub fn safe_report_exec_error(
> // TODO: actual_cmd may be passed as a CStr.
> let actual_cmd: &CStr = unsafe { CStr::from_ptr(actual_cmd) };
> match err {
> - libc::E2BIG => {
> + Errno::E2BIG => {
> let mut sz = 0;
> let mut szenv = 0;
> unsafe {
> @@ -317,7 +318,7 @@ pub fn safe_report_exec_error(
> }
> }
>
> - libc::ENOEXEC => {
> + Errno::ENOEXEC => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process: '",
> @@ -340,7 +341,7 @@ pub fn safe_report_exec_error(
> }
> }
> }
> - libc::EACCES | libc::ENOENT => {
> + Errno::EACCES | Errno::ENOENT => {
> // ENOENT is returned by exec() when the path fails, but also returned by posix_spawn if
> // an open file action fails. These cases appear to be impossible to distinguish. We
> // address this by not using posix_spawn for file redirections, so all the ENOENTs we
> @@ -386,7 +387,7 @@ pub fn safe_report_exec_error(
> actual_cmd,
> "': The file exists and is executable. Check the interpreter or linker?"
> );
> - } else if err == libc::ENOENT {
> + } else if err == Errno::ENOENT {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -403,11 +404,11 @@ pub fn safe_report_exec_error(
> }
> }
>
> - libc::ENOMEM => {
> + Errno::ENOMEM => {
> FLOG_SAFE!(exec, "Out of memory");
> }
>
> - libc::ETXTBSY => {
> + Errno::ETXTBSY => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -416,7 +417,7 @@ pub fn safe_report_exec_error(
> );
> }
>
> - libc::ELOOP => {
> + Errno::ELOOP => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -425,7 +426,7 @@ pub fn safe_report_exec_error(
> );
> }
>
> - libc::EINVAL => {
> + Errno::EINVAL => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -433,7 +434,7 @@ pub fn safe_report_exec_error(
> "': Unsupported format."
> );
> }
> - libc::EISDIR => {
> + Errno::EISDIR => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -441,7 +442,7 @@ pub fn safe_report_exec_error(
> "': File is a directory."
> );
> }
> - libc::ENOTDIR => {
> + Errno::ENOTDIR => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -450,7 +451,7 @@ pub fn safe_report_exec_error(
> );
> }
>
> - libc::EMFILE => {
> + Errno::EMFILE => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -458,7 +459,7 @@ pub fn safe_report_exec_error(
> "': Too many open files in this process."
> );
> }
> - libc::ENFILE => {
> + Errno::ENFILE => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -466,7 +467,7 @@ pub fn safe_report_exec_error(
> "': Too many open files on the system."
> );
> }
> - libc::ENAMETOOLONG => {
> + Errno::ENAMETOOLONG => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -474,7 +475,7 @@ pub fn safe_report_exec_error(
> "': Name is too long."
> );
> }
> - libc::EPERM => {
> + Errno::EPERM => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -485,7 +486,7 @@ pub fn safe_report_exec_error(
> }
>
> #[cfg(target_os = "macos")]
> - libc::EBADARCH => {
> + Errno::EBADARCH => {
> FLOG_SAFE!(
> exec,
> "Failed to execute process '",
> @@ -500,7 +501,7 @@ pub fn safe_report_exec_error(
> "Failed to execute process '",
> actual_cmd,
> "', unknown error number ",
> - err,
> + err as i32,
> );
> }
> }
> @@ -569,7 +570,7 @@ mod ffi {
> argvv: *const *const c_char,
> envv: *const *const c_char,
> ) {
> - super::safe_report_exec_error(err, actual_cmd, argvv, envv)
> + super::safe_report_exec_error(Errno::from_i32(err), actual_cmd, argvv, envv)
> }
>
> #[no_mangle]
> @@ -580,6 +581,8 @@ mod ffi {
> #[no_mangle]
> pub extern "C" fn execute_setpgid(pid: pid_t, pgroup: pid_t, is_parent: bool) -> i32 {
> super::execute_setpgid(pid, pgroup, is_parent)
> + .map(|e| e as i32)
> + .unwrap_or(0)
> }
>
> #[no_mangle]
> @@ -593,7 +596,7 @@ mod ffi {
> argv0_str: *const c_char,
> ) {
> super::report_setpgid_error(
> - err,
> + Errno::from_i32(err),
> is_parent,
> pid,
> desired_pgid,
> diff --git a/fish-rust/src/fork_exec/spawn.rs b/fish-rust/src/fork_exec/spawn.rs
> index 59d9fdaca0e9..d277a1480e81 100644
> --- a/fish-rust/src/fork_exec/spawn.rs
> +++ b/fish-rust/src/fork_exec/spawn.rs
> @@ -4,9 +4,10 @@ use super::blocked_signals_for_job;
> use crate::exec::is_thompson_shell_script;
> use crate::proc::Job;
> use crate::redirection::Dup2List;
> +use crate::set_errno;
> use crate::signal::get_signals_with_handlers;
> -use errno::{self, set_errno, Errno};
> use libc::{self, c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
> +use nix::errno::Errno;
> use std::ffi::{CStr, CString};
>
> // The posix_spawn family of functions is unusual in that it returns errno codes directly in the return value, not via errno.
> @@ -14,7 +15,7 @@ use std::ffi::{CStr, CString};
> fn check_fail(res: i32) -> Result<(), Errno> {
> match res {
> 0 => Ok(()),
> - err => Err(Errno(err)),
> + err => Err(Errno::from_i32(err)),
> }
> }
>
> @@ -181,7 +182,7 @@ impl PosixSpawner {
> // after performing a binary safety check, recommended by POSIX: a
> // line needs to exist before the first \0 with a lowercase letter.
> let cmdcstr = unsafe { CStr::from_ptr(cmd) };
> - if spawn_err.0 == libc::ENOEXEC && is_thompson_shell_script(cmdcstr) {
> + if spawn_err == Errno::ENOEXEC && is_thompson_shell_script(cmdcstr) {
> // Create a new argv with /bin/sh prepended.
> let interp = get_path_bshell();
> let mut argv2 = vec![interp.as_ptr() as *mut c_char];
> diff --git a/fish-rust/src/history.rs b/fish-rust/src/history.rs
> index 3f351e60caa7..b1695fd66589 100644
> --- a/fish-rust/src/history.rs
> +++ b/fish-rust/src/history.rs
> @@ -37,6 +37,7 @@ use libc::{
> O_RDONLY, O_WRONLY, SEEK_SET,
> };
> use lru::LruCache;
> +use nix::errno::Errno;
> use rand::Rng;
> use widestring_suffix::widestrs;
>
> @@ -862,7 +863,7 @@ impl HistoryImpl {
> FLOGF!(
> history_file,
> "Error %d when truncating temporary history file",
> - errno::errno().0
> + Errno::last() as i32,
> );
> }
> } else {
> @@ -882,14 +883,14 @@ impl HistoryImpl {
> FLOGF!(
> history_file,
> "Error %d when changing ownership of history file",
> - errno::errno().0
> + Errno::last() as i32,
> );
> }
> if unsafe { fchmod(tmp_fd, sbuf.st_mode) } == -1 {
> FLOGF!(
> history_file,
> "Error %d when changing mode of history file",
> - errno::errno().0,
> + Errno::last() as i32,
> );
> }
> }
> @@ -898,10 +899,7 @@ impl HistoryImpl {
> if wrename(&tmp_name, &target_name) == -1 {
> FLOG!(
> error,
> - wgettext_fmt!(
> - "Error when renaming history file: %s",
> - errno::errno().to_string()
> - )
> + wgettext_fmt!("Error when renaming history file: %s", Errno::last().desc())
> );
> }
>
> diff --git a/fish-rust/src/history/file.rs b/fish-rust/src/history/file.rs
> index 6ff6785a591e..9d9da4b7bd75 100644
> --- a/fish-rust/src/history/file.rs
> +++ b/fish-rust/src/history/file.rs
> @@ -7,11 +7,11 @@ use std::{
> time::{Duration, SystemTime, UNIX_EPOCH},
> };
>
> -use errno::errno;
> use libc::{
> - c_void, lseek, mmap, munmap, EINTR, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_READ,
> - PROT_WRITE, SEEK_END, SEEK_SET,
> + c_void, lseek, mmap, munmap, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_READ, PROT_WRITE,
> + SEEK_END, SEEK_SET,
> };
> +use nix::errno::Errno;
>
> use crate::{
> common::{str2wcstring, subslice_position, wcs2string},
> @@ -238,7 +238,7 @@ fn read_from_fd(fd: RawFd, dest: &mut [u8]) -> bool {
> let amt =
> unsafe { libc::read(fd, (&mut dest[nread]) as *mut u8 as *mut c_void, remaining) };
> if amt < 0 {
> - if errno().0 != EINTR {
> + if Errno::last() != Errno::EINTR {
> return false;
> }
> } else if amt == 0 {
> diff --git a/fish-rust/src/input.rs b/fish-rust/src/input.rs
> index bdcaebff9892..c5ea8b5a3a85 100644
> --- a/fish-rust/src/input.rs
> +++ b/fish-rust/src/input.rs
> @@ -12,10 +12,11 @@ use crate::proc::job_reap;
> use crate::reader::{
> reader_reading_interrupted, reader_reset_interrupted, reader_schedule_prompt_repaint,
> };
> +use crate::set_errno;
> use crate::signal::signal_clear_cancel;
> use crate::threads::assert_is_main_thread;
> use crate::wchar::prelude::*;
> -use errno::{set_errno, Errno};
> +use nix::errno::Errno;
> use once_cell::sync::{Lazy, OnceCell};
> use std::cell::RefCell;
> use std::collections::VecDeque;
> @@ -1135,7 +1136,7 @@ pub fn input_terminfo_get_sequence(name: &wstr, out_seq: &mut WString) -> bool {
> if name == m.name {
> // Found the mapping.
> if m.seq.is_none() {
> - set_errno(Errno(libc::EILSEQ));
> + set_errno(Errno::EILSEQ);
> return false;
> } else {
> *out_seq = str2wcstring(m.seq.as_ref().unwrap());
> @@ -1143,7 +1144,7 @@ pub fn input_terminfo_get_sequence(name: &wstr, out_seq: &mut WString) -> bool {
> }
> }
> }
> - set_errno(Errno(libc::ENOENT));
> + set_errno(Errno::ENOENT);
> false
> }
>
> diff --git a/fish-rust/src/input_common.rs b/fish-rust/src/input_common.rs
> index 14a46fd65555..ef55920f722f 100644
> --- a/fish-rust/src/input_common.rs
> +++ b/fish-rust/src/input_common.rs
> @@ -8,6 +8,7 @@ use crate::universal_notifier::default_notifier;
> use crate::wchar::prelude::*;
> use crate::wutil::encoding::{mbrtowc, zero_mbstate};
> use crate::wutil::fish_wcstol;
> +use nix::errno::Errno;
> use std::collections::VecDeque;
> use std::os::fd::RawFd;
> use std::ptr;
> @@ -176,8 +177,8 @@ fn readb(in_fd: RawFd) -> ReadbResult {
> // Here's where we call select().
> let select_res = fdset.check_readable(FdReadableSet::kNoTimeout);
> if select_res < 0 {
> - let err = errno::errno().0;
> - if err == libc::EINTR || err == libc::EAGAIN {
> + let err = Errno::last();
> + if err == Errno::EINTR || err == Errno::EAGAIN {
> // A signal.
> return ReadbResult::Interrupted;
> } else {
> diff --git a/fish-rust/src/io.rs b/fish-rust/src/io.rs
> index ac1b469e2346..e660c0c8ed66 100644
> --- a/fish-rust/src/io.rs
> +++ b/fish-rust/src/io.rs
> @@ -18,8 +18,8 @@ use crate::wchar::prelude::*;
> use crate::wchar_ffi::WCharFromFFI;
> use crate::wutil::{perror, perror_io, wdirname, wstat, wwrite_to_fd};
> use cxx::CxxWString;
> -use errno::Errno;
> -use libc::{EAGAIN, EEXIST, EINTR, ENOENT, ENOTDIR, EPIPE, EWOULDBLOCK, O_EXCL, STDOUT_FILENO};
> +use libc::{O_EXCL, STDOUT_FILENO};
> +use nix::errno::Errno;
> use std::cell::{RefCell, UnsafeCell};
> use std::os::fd::RawFd;
> use std::sync::atomic::{AtomicU64, Ordering};
> @@ -478,7 +478,7 @@ impl IoBuffer {
> /// set).
> pub fn read_once(fd: RawFd, buffer: &mut MutexGuard<'_, SeparatedBuffer>) -> isize {
> assert!(fd >= 0, "Invalid fd");
> - errno::set_errno(Errno(0));
> + Errno::clear();
> let mut bytes = [b'\0'; 4096 * 4];
>
> // We want to swallow EINTR only; in particular EAGAIN needs to be returned back to the caller.
> @@ -490,12 +490,12 @@ impl IoBuffer {
> std::mem::size_of_val(&bytes),
> )
> };
> - if amt < 0 && errno::errno().0 == EINTR {
> + if amt < 0 && Errno::last() == Errno::EINTR {
> continue;
> }
> break amt;
> };
> - if amt < 0 && ![EAGAIN, EWOULDBLOCK].contains(&errno::errno().0) {
> + if amt < 0 && ![Errno::EAGAIN, Errno::EWOULDBLOCK].contains(&Errno::last()) {
> perror("read");
> } else if amt > 0 {
> buffer.append(
> @@ -582,8 +582,9 @@ fn begin_filling(iobuffer: &Arc<IoBuffer>, fd: AutoCloseFd) {
> // select() reported us as readable; read a bit.
> let mut buf = iobuffer.buffer.lock().unwrap();
> let ret = IoBuffer::read_once(fd.fd(), &mut buf);
> - done =
> - ret == 0 || (ret < 0 && ![EAGAIN, EWOULDBLOCK].contains(&errno::errno().0));
> + done = ret == 0
> + || (ret < 0
> + && ![Errno::EAGAIN, Errno::EWOULDBLOCK].contains(&Errno::last()));
> } else if iobuffer.shutdown_fillthread.load() {
> // Here our caller asked us to shut down; read while we keep getting data.
> // This will stop when the fd is closed or if we get EAGAIN.
> @@ -676,16 +677,16 @@ impl IoChain {
> let oflags = spec.oflags();
> let file = AutoCloseFd::new(wopen_cloexec(&path, oflags, OPEN_MASK));
> if !file.is_valid() {
> - if (oflags & O_EXCL) != 0 && errno::errno().0 == EEXIST {
> + if (oflags & O_EXCL) != 0 && Errno::last() == Errno::EEXIST {
> FLOGF!(warning, NOCLOB_ERROR, spec.target);
> } else {
> if should_flog!(warning) {
> FLOGF!(warning, FILE_ERROR, spec.target);
> - let err = errno::errno().0;
> + let err = Errno::last();
> // If the error is that the file doesn't exist
> // or there's a non-directory component,
> // find the first problematic component for a better message.
> - if [ENOENT, ENOTDIR].contains(&err) {
> + if [Errno::ENOENT, Errno::ENOTDIR].contains(&err) {
> let mut dname: &wstr = &spec.target;
> while !dname.is_empty() {
> let next: &wstr = wdirname(dname);
> @@ -875,14 +876,14 @@ impl FdOutputStream {
> // Some of our builtins emit multiple screens worth of data sent to a pager (the primary
> // example being the `history` builtin) and receiving SIGINT should be considered normal and
> // non-exceptional (user request to abort via Ctrl-C), meaning we shouldn't print an error.
> - if errno::errno().0 == EINTR && self.sigcheck.check() {
> + if Errno::last() == Errno::EINTR && self.sigcheck.check() {
> // We have two options here: we can either return false without setting errored_ to
> // true (*this* write will be silently aborted but the onus is on the caller to check
> // the return value and skip future calls to `append()`) or we can flag the entire
> // output stream as errored, causing us to both return false and skip any future writes.
> // We're currently going with the latter, especially seeing as no callers currently
> // check the result of `append()` (since it was always a void function before).
> - } else if errno::errno().0 != EPIPE {
> + } else if Errno::last() != Errno::EPIPE {
> perror("write");
> }
> self.errored = true;
> diff --git a/fish-rust/src/lib.rs b/fish-rust/src/lib.rs
> index 409f1f029aea..947a8d2723c8 100644
> --- a/fish-rust/src/lib.rs
> +++ b/fish-rust/src/lib.rs
> @@ -121,3 +121,9 @@ mod wutil;
> #[cfg(any(test, feature = "fish-ffi-tests"))]
> #[allow(unused_imports)] // Easy way to suppress warnings while we have two testing modes.
> mod tests;
> +
> +// TODO: Remove once nix is updated to include set_errno
> +// https://github.com/nix-rust/nix/pull/2283
> +fn set_errno(errno: ::nix::errno::Errno) {
> + ::_errno::set_errno(::_errno::Errno(errno as i32))
> +}
> diff --git a/fish-rust/src/parse_execution.rs b/fish-rust/src/parse_execution.rs
> index c2a5372a8601..e79c273707c3 100644
> --- a/fish-rust/src/parse_execution.rs
> +++ b/fish-rust/src/parse_execution.rs
> @@ -799,7 +799,7 @@ impl<'a> ParseExecutionContext {
> &external_cmd.path
> },
> statement,
> - std::io::Error::from_raw_os_error(external_cmd.err.unwrap().into()),
> + std::io::Error::from(external_cmd.err.unwrap()),
> );
> }
> };
> diff --git a/fish-rust/src/path.rs b/fish-rust/src/path.rs
> index 4877a975ca74..72c9f994890c 100644
> --- a/fish-rust/src/path.rs
> +++ b/fish-rust/src/path.rs
> @@ -8,10 +8,11 @@ use crate::compat::{MNT_LOCAL, ST_LOCAL};
> use crate::env::{EnvMode, EnvStack, Environment};
> use crate::expand::{expand_tilde, HOME_DIRECTORY};
> use crate::flog::{FLOG, FLOGF};
> +use crate::set_errno;
> use crate::wchar::prelude::*;
> use crate::wutil::{normalize_path, path_normalize_for_cd, waccess, wdirname, wmkdir, wstat};
> -use errno::{errno, set_errno, Errno};
> -use libc::{EACCES, ENOENT, ENOTDIR, F_OK, X_OK};
> +use libc::{F_OK, X_OK};
> +use nix::errno::Errno;
> use once_cell::sync::Lazy;
> use std::ffi::OsStr;
> use std::io::ErrorKind;
> @@ -117,7 +118,7 @@ fn maybe_issue_path_warning(
> using_xdg: bool,
> xdg_var: &wstr,
> path: &wstr,
> - saved_errno: libc::c_int,
> + saved_errno: Errno,
> vars: &EnvStack,
> ) {
> let warning_var_name = "_FISH_WARNED_"L.to_owned() + which_dir;
> @@ -159,7 +160,7 @@ fn maybe_issue_path_warning(
> );
> FLOG!(
> warning_path,
> - wgettext_fmt!("The error was '%s'.", Errno(saved_errno).to_string())
> + wgettext_fmt!("The error was '%s'.", saved_errno.desc())
> );
> FLOG!(
> warning_path,
> @@ -262,22 +263,22 @@ pub fn path_get_paths(cmd: &wstr, vars: &dyn Environment) -> Vec<WString> {
> }
>
> fn path_get_path_core<S: AsRef<wstr>>(cmd: &wstr, pathsv: &[S]) -> GetPathResult {
> - let noent_res = GetPathResult::new(Some(Errno(ENOENT)), WString::new());
> + let noent_res = GetPathResult::new(Some(Errno::ENOENT), WString::new());
> // Test if the given path can be executed.
> // \return 0 on success, an errno value on failure.
> let test_path = |path: &wstr| -> Result<(), Errno> {
> let narrow = wcs2zstring(path);
> if unsafe { libc::access(narrow.as_ptr(), X_OK) } != 0 {
> - return Err(errno());
> + return Err(Errno::last());
> }
> let narrow: Vec<u8> = narrow.into();
> let Ok(md) = std::fs::metadata(OsStr::from_bytes(&narrow)) else {
> - return Err(errno());
> + return Err(Errno::last());
> };
> if md.is_file() {
> Ok(())
> } else {
> - Err(Errno(EACCES))
> + Err(Errno::EACCES)
> }
> };
>
> @@ -310,7 +311,7 @@ fn path_get_path_core<S: AsRef<wstr>>(cmd: &wstr, pathsv: &[S]) -> GetPathResult
> return GetPathResult::new(None, proposed_path);
> }
> Err(err) => {
> - if err.0 != ENOENT && best.err == Some(Errno(ENOENT)) {
> + if err != Errno::ENOENT && best.err == Some(Errno::ENOENT) {
> // Keep the first *interesting* error and path around.
> // ENOENT isn't interesting because not having a file is the normal case.
> // Ignore if the parent directory is already inaccessible.
> @@ -336,7 +337,7 @@ fn path_get_path_core<S: AsRef<wstr>>(cmd: &wstr, pathsv: &[S]) -> GetPathResult
> /// \param vars The environment variables to use (for the CDPATH variable)
> /// \return the command, or none() if it could not be found.
> pub fn path_get_cdpath(dir: &wstr, wd: &wstr, vars: &dyn Environment) -> Option<WString> {
> - let mut err = ENOENT;
> + let mut err = Errno::ENOENT;
> if dir.is_empty() {
> return None;
> }
> @@ -348,11 +349,11 @@ pub fn path_get_cdpath(dir: &wstr, wd: &wstr, vars: &dyn Environment) -> Option<
> if md.is_dir() {
> return Some(a_dir);
> }
> - err = ENOTDIR;
> + err = Errno::ENOTDIR;
> }
> }
>
> - set_errno(Errno(err));
> + set_errno(err);
> None
> }
>
> @@ -568,14 +569,14 @@ struct BaseDirectory {
> /// whether the dir is remote
> remoteness: DirRemoteness,
> /// the error code if creating the directory failed, or 0 on success.
> - err: libc::c_int,
> + err: Errno,
> /// whether an XDG variable was used in resolving the directory.
> used_xdg: bool,
> }
>
> impl BaseDirectory {
> fn success(&self) -> bool {
> - self.err == 0
> + self.err == Errno::from_i32(0)
> }
> }
>
> @@ -601,15 +602,15 @@ fn make_base_directory(xdg_var: &wstr, non_xdg_homepath: &wstr) -> BaseDirectory
> used_xdg = false;
> }
>
> - set_errno(Errno(0));
> + Errno::clear();
> let err;
> let mut remoteness = DirRemoteness::unknown;
> if path.is_empty() {
> - err = ENOENT;
> + err = Errno::ENOENT;
> } else if !create_directory(&path) {
> - err = errno().0;
> + err = Errno::last();
> } else {
> - err = 0;
> + err = Errno::from_i32(0);
> // Need to append a trailing slash to check the contents of the directory, not its parent.
> let mut tmp = path.clone();
> tmp.push('/');
> diff --git a/fish-rust/src/proc.rs b/fish-rust/src/proc.rs
> index f623b7e0e692..987f3a9bfb98 100644
> --- a/fish-rust/src/proc.rs
> +++ b/fish-rust/src/proc.rs
> @@ -24,11 +24,11 @@ use crate::wchar::{wstr, WString, L};
> use crate::wchar_ext::ToWString;
> use crate::wutil::{perror, wbasename, wgettext, wperror};
> use libc::{
> - EBADF, EINVAL, ENOTTY, EPERM, EXIT_SUCCESS, SIGABRT, SIGBUS, SIGCONT, SIGFPE, SIGHUP, SIGILL,
> - SIGINT, SIGPIPE, SIGQUIT, SIGSEGV, SIGSYS, SIGTTOU, SIG_DFL, SIG_IGN, STDIN_FILENO, WCONTINUED,
> - WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WTERMSIG, WUNTRACED,
> - _SC_CLK_TCK,
> + EXIT_SUCCESS, SIGABRT, SIGBUS, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGPIPE, SIGQUIT,
> + SIGSEGV, SIGSYS, SIGTTOU, SIG_DFL, SIG_IGN, STDIN_FILENO, WCONTINUED, WEXITSTATUS,
> + WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WTERMSIG, WUNTRACED, _SC_CLK_TCK,
> };
> +use nix::errno::Errno;
> use once_cell::sync::Lazy;
> use printf_compat::sprintf;
> use std::cell::{Cell, Ref, RefCell, RefMut};
> @@ -353,7 +353,7 @@ impl TtyTransfer {
> let mut tmodes: libc::termios = unsafe { std::mem::zeroed() };
> if unsafe { libc::tcgetattr(STDIN_FILENO, &mut tmodes) } == 0 {
> owner.tmodes.replace(Some(tmodes));
> - } else if errno::errno().0 != ENOTTY {
> + } else if Errno::last() != Errno::ENOTTY {
> perror("tcgetattr");
> }
> }
> @@ -410,7 +410,7 @@ impl TtyTransfer {
> // guarantee the process isn't going to exit while we wait (which would cause us to possibly
> // block indefinitely).
> while unsafe { libc::tcsetpgrp(STDIN_FILENO, pgid) } != 0 {
> - FLOGF!(proc_termowner, "tcsetpgrp failed: %d", errno::errno().0);
> + FLOGF!(proc_termowner, "tcsetpgrp failed: %d", Errno::last() as i32);
>
> // Before anything else, make sure that it's even necessary to call tcsetpgrp.
> // Since it usually _is_ necessary, we only check in case it fails so as to avoid the
> @@ -418,13 +418,13 @@ impl TtyTransfer {
> // a significant cost when running process groups in quick succession.
> let getpgrp_res = unsafe { libc::tcgetpgrp(STDIN_FILENO) };
> if getpgrp_res < 0 {
> - match errno::errno().0 {
> - ENOTTY => {
> + match Errno::last() {
> + Errno::ENOTTY => {
> // stdin is not a tty. This may come about if job control is enabled but we are
> // not a tty - see #6573.
> return false;
> }
> - EBADF => {
> + Errno::EBADF => {
> // stdin has been closed. Workaround a glibc bug - see #3644.
> redirect_tty_output();
> return false;
> @@ -445,46 +445,52 @@ impl TtyTransfer {
> }
>
> let pgroup_terminated;
> - if errno::errno().0 == EINVAL {
> - // OS X returns EINVAL if the process group no longer lives. Probably other OSes,
> - // too. Unlike EPERM below, EINVAL can only happen if the process group has
> - // terminated.
> - pgroup_terminated = true;
> - } else if errno::errno().0 == EPERM {
> - // Retry so long as this isn't because the process group is dead.
> - let mut result: libc::c_int = 0;
> - let wait_result = unsafe { libc::waitpid(-pgid, &mut result, WNOHANG) };
> - if wait_result == -1 {
> - // Note that -1 is technically an "error" for waitpid in the sense that an
> - // invalid argument was specified because no such process group exists any
> - // longer. This is the observed behavior on Linux 4.4.0. a "success" result
> - // would mean processes from the group still exist but is still running in some
> - // state or the other.
> +
> + match Errno::last() {
> + Errno::EINVAL => {
> + // OS X returns EINVAL if the process group no longer lives. Probably other OSes,
> + // too. Unlike EPERM below, EINVAL can only happen if the process group has
> + // terminated.
> pgroup_terminated = true;
> - } else {
> - // Debug the original tcsetpgrp error (not the waitpid errno) to the log, and
> - // then retry until not EPERM or the process group has exited.
> + }
> + Errno::EPERM => {
> + // Retry so long as this isn't because the process group is dead.
> + let mut result: libc::c_int = 0;
> + let wait_result = unsafe { libc::waitpid(-pgid, &mut result, WNOHANG) };
> + if wait_result == -1 {
> + // Note that -1 is technically an "error" for waitpid in the sense that an
> + // invalid argument was specified because no such process group exists any
> + // longer. This is the observed behavior on Linux 4.4.0. a "success" result
> + // would mean processes from the group still exist but is still running in some
> + // state or the other.
> + pgroup_terminated = true;
> + } else {
> + // Debug the original tcsetpgrp error (not the waitpid errno) to the log, and
> + // then retry until not EPERM or the process group has exited.
> + FLOGF!(
> + proc_termowner,
> + "terminal_give_to_job(): EPERM with pgid %d.",
> + pgid
> + );
> + continue;
> + }
> + }
> + Errno::ENOTTY => {
> + // stdin is not a TTY. In general we expect this to be caught via the tcgetpgrp
> + // call's EBADF handler above.
> + return false;
> + }
> + _ => {
> FLOGF!(
> - proc_termowner,
> - "terminal_give_to_job(): EPERM with pgid %d.",
> + warning,
> + "Could not send job %d ('%ls') with pgid %d to foreground",
> + jg.job_id.to_wstring(),
> + jg.command,
> pgid
> );
> - continue;
> + perror("tcsetpgrp");
> + return false;
> }
> - } else if errno::errno().0 == ENOTTY {
> - // stdin is not a TTY. In general we expect this to be caught via the tcgetpgrp
> - // call's EBADF handler above.
> - return false;
> - } else {
> - FLOGF!(
> - warning,
> - "Could not send job %d ('%ls') with pgid %d to foreground",
> - jg.job_id.to_wstring(),
> - jg.command,
> - pgid
> - );
> - perror("tcsetpgrp");
> - return false;
> }
>
> if pgroup_terminated {
> diff --git a/fish-rust/src/reader.rs b/fish-rust/src/reader.rs
> index cf2a6c6dc392..032aabeb252d 100644
> --- a/fish-rust/src/reader.rs
> +++ b/fish-rust/src/reader.rs
> @@ -13,9 +13,9 @@
> //! end of the list is reached, at which point regular searching will commence.
>
> use libc::{
> - c_char, c_int, c_void, EAGAIN, ECHO, EINTR, EIO, EISDIR, ENOTTY, EPERM, ESRCH, EWOULDBLOCK,
> - ICANON, ICRNL, IEXTEN, INLCR, IXOFF, IXON, ONLCR, OPOST, O_NONBLOCK, O_RDONLY, SIGINT, SIGTTIN,
> - STDIN_FILENO, STDOUT_FILENO, S_IFDIR, TCSANOW, VMIN, VQUIT, VSUSP, VTIME, _POSIX_VDISABLE,
> + c_char, c_int, c_void, ECHO, ICANON, ICRNL, IEXTEN, INLCR, IXOFF, IXON, ONLCR, OPOST,
> + O_NONBLOCK, O_RDONLY, SIGINT, SIGTTIN, STDIN_FILENO, STDOUT_FILENO, S_IFDIR, TCSANOW, VMIN,
> + VQUIT, VSUSP, VTIME, _POSIX_VDISABLE,
> };
> use once_cell::sync::Lazy;
> use std::cell::UnsafeCell;
> @@ -30,7 +30,7 @@ use std::sync::atomic::{AtomicI32, AtomicU32, AtomicU64, AtomicU8};
> use std::sync::{Arc, Mutex, MutexGuard};
> use std::time::{Duration, Instant};
>
> -use errno::{errno, Errno};
> +use nix::errno::Errno;
>
> use crate::abbrs::abbrs_match;
> use crate::ast::{self, Ast, Category, Traversal};
> @@ -546,7 +546,9 @@ pub fn reader_read(parser: &Parser, fd: RawFd, io: &IoChain) -> c_int {
> let a_tty = unsafe { libc::isatty(STDIN_FILENO) } != 0;
> if a_tty {
> interactive = true;
> - } else if unsafe { libc::tcgetattr(STDIN_FILENO, &mut t) } == -1 && errno().0 == EIO {
> + } else if unsafe { libc::tcgetattr(STDIN_FILENO, &mut t) } == -1
> + && Errno::last() == Errno::EIO
> + {
> redirect_tty_output();
> interactive = true;
> }
> @@ -662,10 +664,10 @@ fn read_i(parser: &Parser) -> i32 {
> fn read_ni(parser: &Parser, fd: RawFd, io: &IoChain) -> i32 {
> let mut buf: libc::stat = unsafe { std::mem::zeroed() };
> if unsafe { libc::fstat(fd, &mut buf) } == -1 {
> - let err = errno();
> + let err = Errno::last();
> FLOG!(
> error,
> - wgettext_fmt!("Unable to read input file: %s", err.to_string())
> + wgettext_fmt!("Unable to read input file: %s", err.desc())
> );
> return 1;
> }
> @@ -676,7 +678,7 @@ fn read_ni(parser: &Parser, fd: RawFd, io: &IoChain) -> i32 {
> if fd != STDIN_FILENO && (buf.st_mode & S_IFDIR) != 0 {
> FLOG!(
> error,
> - wgettext_fmt!("Unable to read input file: %s", Errno(EISDIR).to_string())
> + wgettext_fmt!("Unable to read input file: %s", Errno::EISDIR.desc())
> );
> return 1;
> }
> @@ -693,17 +695,19 @@ fn read_ni(parser: &Parser, fd: RawFd, io: &IoChain) -> i32 {
> break;
> } else {
> assert!(amt == -1);
> - let err = errno();
> - if err.0 == EINTR {
> + let err = Errno::last();
> + if err == Errno::EINTR {
> continue;
> - } else if err.0 == EAGAIN || err.0 == EWOULDBLOCK && make_fd_blocking(fd).is_ok() {
> + } else if err == Errno::EAGAIN
> + || err == Errno::EWOULDBLOCK && make_fd_blocking(fd).is_ok()
> + {
> // We succeeded in making the fd blocking, keep going.
> continue;
> } else {
> // Fatal error.
> FLOG!(
> error,
> - wgettext_fmt!("Unable to read input file: %s", err.to_string())
> + wgettext_fmt!("Unable to read input file: %s", err.desc())
> );
> return 1;
> }
> @@ -784,7 +788,7 @@ pub fn restore_term_mode() {
> TCSANOW,
> &*TERMINAL_MODE_ON_STARTUP.lock().unwrap(),
> ) == -1
> - } && errno().0 == EIO
> + } && Errno::last() == Errno::EIO
> {
> redirect_tty_output();
> }
> @@ -1690,21 +1694,23 @@ impl ReaderData {
>
> // Get the current terminal modes. These will be restored when the function returns.
> let mut old_modes: libc::termios = unsafe { std::mem::zeroed() };
> - if unsafe { libc::tcgetattr(zelf.conf.inputfd, &mut old_modes) } == -1 && errno().0 == EIO {
> + if unsafe { libc::tcgetattr(zelf.conf.inputfd, &mut old_modes) } == -1
> + && Errno::last() == Errno::EIO
> + {
> redirect_tty_output();
> }
>
> // Set the new modes.
> if unsafe { libc::tcsetattr(zelf.conf.inputfd, TCSANOW, shell_modes()) } == -1 {
> - let err = errno().0;
> - if err == EIO {
> + let err = Errno::last();
> + if err == Errno::EIO {
> redirect_tty_output();
> }
>
> // This check is required to work around certain issues with fish's approach to
> // terminal control when launching interactive processes while in non-interactive
> // mode. See #4178 for one such example.
> - if err != ENOTTY || is_interactive_session() {
> + if err != Errno::ENOTTY || is_interactive_session() {
> perror("tcsetattr");
> }
> }
> @@ -1897,7 +1903,7 @@ impl ReaderData {
> if unsafe { libc::tcsetattr(zelf.conf.inputfd, TCSANOW, &old_modes) } == -1
> && is_interactive_session()
> {
> - if errno().0 == EIO {
> + if Errno::last() == Errno::EIO {
> redirect_tty_output();
> }
> perror("tcsetattr"); // return to previous mode
> @@ -1927,7 +1933,7 @@ impl ReaderData {
> let mut res;
> loop {
> res = unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, shell_modes_mut()) };
> - if res >= 0 || errno().0 != EINTR {
> + if res >= 0 || Errno::last() != Errno::EINTR {
> break;
> }
> }
> @@ -3298,10 +3304,10 @@ fn term_donate(quiet: bool /* = false */) {
> )
> } == -1
> {
> - if errno().0 == EIO {
> + if Errno::last() == Errno::EIO {
> redirect_tty_output();
> }
> - if errno().0 != EINTR {
> + if Errno::last() != Errno::EINTR {
> if !quiet {
> FLOG!(
> warning,
> @@ -3338,10 +3344,10 @@ pub fn term_copy_modes() {
> fn term_steal() {
> term_copy_modes();
> while unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, shell_modes()) } == -1 {
> - if errno().0 == EIO {
> + if Errno::last() == Errno::EIO {
> redirect_tty_output();
> }
> - if errno().0 != EINTR {
> + if Errno::last() != Errno::EINTR {
> FLOG!(warning, wgettext!("Could not set terminal mode for shell"));
> perror("tcsetattr");
> break;
> @@ -3403,7 +3409,7 @@ fn acquire_tty_or_exit(shell_pgid: libc::pid_t) {
> // avoid a second pass through this loop.
> owner = unsafe { libc::tcgetpgrp(STDIN_FILENO) };
> }
> - if owner == -1 && errno().0 == ENOTTY {
> + if owner == -1 && Errno::last() == Errno::ENOTTY {
> if !is_interactive_session() {
> // It's OK if we're not able to take control of the terminal. We handle
> // the fallout from this in a few other places.
> @@ -3463,7 +3469,7 @@ fn reader_interactive_init(parser: &Parser) {
> // don't apply as we passed our own pid.
> //
> // This should be harmless, so we ignore it.
> - if errno().0 != EPERM {
> + if Errno::last() != Errno::EPERM {
> FLOG!(
> error,
> wgettext!("Failed to assign shell to its own process group")
> @@ -3475,7 +3481,7 @@ fn reader_interactive_init(parser: &Parser) {
>
> // Take control of the terminal
> if unsafe { libc::tcsetpgrp(STDIN_FILENO, shell_pgid) } == -1 {
> - if errno().0 == ENOTTY {
> + if Errno::last() == Errno::ENOTTY {
> redirect_tty_output();
> }
> FLOG!(error, wgettext!("Failed to take control of the terminal"));
> @@ -3485,7 +3491,7 @@ fn reader_interactive_init(parser: &Parser) {
>
> // Configure terminal attributes
> if unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, shell_modes_mut()) } == -1 {
> - if errno().0 == EIO {
> + if Errno::last() == Errno::EIO {
> redirect_tty_output();
> }
> FLOG!(warning, wgettext!("Failed to set startup terminal mode!"));
> @@ -4541,7 +4547,10 @@ fn check_for_orphaned_process(loop_count: usize, shell_pgid: libc::pid_t) -> boo
> // Try kill-0'ing the process whose pid corresponds to our process group ID. It's possible this
> // will fail because we don't have permission to signal it. But more likely it will fail because
> // it no longer exists, and we are orphaned.
> - if loop_count % 64 == 0 && unsafe { libc::kill(shell_pgid, 0) } < 0 && errno().0 == ESRCH {
> + if loop_count % 64 == 0
> + && unsafe { libc::kill(shell_pgid, 0) } < 0
> + && Errno::last() == Errno::ESRCH
> + {
> we_think_we_are_orphaned = true;
> }
>
> @@ -4572,7 +4581,7 @@ fn check_for_orphaned_process(loop_count: usize, shell_pgid: libc::pid_t) -> boo
> 1,
> )
> } < 0
> - && errno().0 == EIO
> + && Errno::last() == Errno::EIO
> {
> we_think_we_are_orphaned = true;
> }
> diff --git a/fish-rust/src/signal.rs b/fish-rust/src/signal.rs
> index 60a0ef45c35f..f827cba9d710 100644
> --- a/fish-rust/src/signal.rs
> +++ b/fish-rust/src/signal.rs
> @@ -4,13 +4,14 @@ use crate::common::{exit_without_destructors, restore_term_foreground_process_gr
> use crate::event::{enqueue_signal, is_signal_observed};
> use crate::nix::getpid;
> use crate::reader::{reader_handle_sigint, reader_sighup};
> +use crate::set_errno;
> use crate::termsize::TermsizeContainer;
> use crate::topic_monitor::{generation_t, topic_monitor_principal, topic_t, GenerationsList};
> use crate::wchar::prelude::*;
> use crate::wchar_ffi::{AsWstr, WCharToFFI};
> use crate::wutil::{fish_wcstoi, perror};
> use cxx::{CxxWString, UniquePtr};
> -use errno::{errno, set_errno};
> +use nix::errno::Errno;
> use std::sync::atomic::{AtomicI32, Ordering};
>
> #[cxx::bridge]
> @@ -115,7 +116,7 @@ extern "C" fn fish_signal_handler(
> _context: *mut libc::c_void,
> ) {
> // Ensure we preserve errno.
> - let saved_errno = errno();
> + let saved_errno = Errno::last();
>
> // Check if we are a forked child.
> if reraise_if_forked_child(sig) {
> diff --git a/fish-rust/src/threads.rs b/fish-rust/src/threads.rs
> index fd99ca5cdb0d..619c0f29025a 100644
> --- a/fish-rust/src/threads.rs
> +++ b/fish-rust/src/threads.rs
> @@ -3,6 +3,7 @@
>
> use crate::flog::{FloggableDebug, FLOG};
> use crate::reader::ReaderData;
> +use nix::errno::Errno;
> use once_cell::race::OnceBox;
> use std::num::NonZeroU64;
> use std::sync::atomic::{AtomicBool, Ordering};
> @@ -140,7 +141,7 @@ pub fn init() {
> }
> unsafe {
> let result = libc::pthread_atfork(None, None, Some(child_post_fork));
> - assert_eq!(result, 0, "pthread_atfork() failure: {}", errno::errno());
> + assert_eq!(result, 0, "pthread_atfork() failure: {}", Errno::last());
> }
>
> IO_THREAD_POOL
> diff --git a/fish-rust/src/wutil/dir_iter.rs b/fish-rust/src/wutil/dir_iter.rs
> index 8fdbc940b755..b0284d359f1b 100644
> --- a/fish-rust/src/wutil/dir_iter.rs
> +++ b/fish-rust/src/wutil/dir_iter.rs
> @@ -2,10 +2,10 @@ use super::wopendir;
> use crate::common::{cstr2wcstring, wcs2zstring};
> use crate::wchar::{wstr, WString};
> use libc::{
> - DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, EACCES, EIO, ELOOP, ENAMETOOLONG,
> - ENODEV, ENOENT, ENOTDIR, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG,
> - S_IFSOCK,
> + DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO,
> + S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK,
> };
> +use nix::errno::Errno;
> use std::cell::Cell;
> use std::io::{self};
> use std::os::fd::RawFd;
> @@ -107,7 +107,8 @@ impl DirEntry {
> self.stat.set(Some(s));
> self.typ.set(stat_mode_to_entry_type(s.st_mode));
> } else {
> - match errno::errno().0 {
> + use Errno::*;
> + match Errno::last() {
> ELOOP => {
> self.typ.set(Some(DirEntryType::lnk));
> }
> @@ -245,15 +246,15 @@ impl DirIter {
> /// This returns an error if readir errors, or Ok(None) if there are no more entries; else an Ok entry.
> /// This is slightly more efficient than the Iterator version, as it avoids allocating.
> pub fn next(&mut self) -> Option<io::Result<&DirEntry>> {
> - errno::set_errno(errno::Errno(0));
> + Errno::clear();
> let dent = unsafe { libc::readdir(self.dir.dir()).as_ref() };
> let Some(dent) = dent else {
> // readdir distinguishes between EOF and error via errno.
> - let err = errno::errno().0;
> - if err == 0 {
> + let err = Errno::last();
> + if err == Errno::from_i32(0) {
> return None;
> } else {
> - return Some(Err(io::Error::from_raw_os_error(err)));
> + return Some(Err(io::Error::from(err)));
> }
> };
>
> @@ -373,8 +374,8 @@ fn test_dir_iter() {
> let Err(err) = baditer else {
> panic!("Expected error");
> };
> - let err = err.raw_os_error().expect("Should have an errno value");
> - assert!(err == ENOENT || err == EACCES);
> + let err = Errno::try_from(err).expect("Should have an errno value");
> + assert!(err == Errno::ENOENT || err == Errno::EACCES);
>
> let mut t1: [u8; 31] = *b"/tmp/fish_test_dir_iter.XXXXXX\0";
> let basepath_narrow = unsafe { libc::mkdtemp(t1.as_mut_ptr().cast()) };
> diff --git a/fish-rust/src/wutil/mod.rs b/fish-rust/src/wutil/mod.rs
> index dfc03b62ffa8..425c93d88c29 100644
> --- a/fish-rust/src/wutil/mod.rs
> +++ b/fish-rust/src/wutil/mod.rs
> @@ -19,8 +19,8 @@ use crate::flog::FLOGF;
> use crate::wchar::{wstr, WString, L};
> use crate::wchar_ext::WExt;
> use crate::wcstringutil::{join_strings, split_string, wcs2string_callback};
> -use errno::errno;
> pub(crate) use gettext::{wgettext, wgettext_fmt, wgettext_maybe_fmt, wgettext_str};
> +use nix::errno::Errno;
> pub(crate) use printf::sprintf;
> use std::ffi::{CStr, OsStr};
> use std::fs::{self, canonicalize};
> @@ -68,13 +68,13 @@ pub fn wperror(s: &wstr) {
>
> /// Port of the wide-string wperror from `src/wutil.cpp` but for rust `&str`.
> pub fn perror(s: &str) {
> - let e = errno().0;
> + let e = Errno::last();
The weird thing about `nix::errno::Errno` is that they treat "unknown error" the same as "no error".
This could cause subtle issues if the OS ever returns an error that hasn't been added to `nix` yet.
Unless they have a way of guaranteeing consistency with the host libc?
---
I'm not sure `enum` is the right type for `Errno`.
It's already marked `#[non_exhaustive]` so client code can't do exhaustive matching.
I guess they could make it:
```
enum Errno {
NoError = 0
EPERM = libc::EPERM,
...
UnknownErrno(i32),
}
```
but at that point it's probably better to use a struct that wraps `libc::c_int` (or `i32`?), similar to `std::io::Error`.
---
I guess we could use the `nix::errno::Errno::last_raw()` (which is `nix::errno::errno()` today) and `nix::errno::Errno::set_raw()` precisely in the cases where we don't want to ignore unknown errors.
But the problem of being easy to misuse remains.
> let mut stderr = std::io::stderr().lock();
> if !s.is_empty() {
> let _ = write!(stderr, "{s}: ");
> }
> let slice = unsafe {
> - let msg = libc::strerror(e) as *const u8;
> + let msg = libc::strerror(e as i32) as *const u8;
> let len = libc::strlen(msg as *const _);
> std::slice::from_raw_parts(msg, len)
> };
> @@ -102,8 +102,8 @@ pub fn wgetcwd() -> WString {
> FLOGF!(
> error,
> "getcwd() failed with errno %d/%s",
> - errno::errno().0,
> - errno::errno().to_string()
> + Errno::last() as i32,
> + Errno::last().desc()
> );
> WString::new()
> }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment