Skip to main content

yash_env/
builtin.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Type definitions for built-in utilities
18//!
19//! This module provides data types for defining built-in utilities.
20//!
21//! Note that concrete implementations of built-ins are not included in the
22//! `yash_env` crate. For implementations of specific built-ins like `cd` and
23//! `export`, see the `yash_builtin` crate.
24
25use crate::Env;
26#[cfg(doc)]
27use crate::semantics::Divert;
28use crate::semantics::ExitStatus;
29use crate::semantics::Field;
30use std::fmt::Debug;
31use std::pin::Pin;
32
33/// Types of built-in utilities
34#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
35pub enum Type {
36    /// Special built-in
37    ///
38    /// Special built-in utilities are built-ins that are defined in [POSIX XCU
39    /// section 2.15](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_15).
40    ///
41    /// They are treated differently from other built-ins.
42    /// Especially, special built-ins are found in the first stage of command
43    /// search without the `$PATH` search and cannot be overridden by functions
44    /// or external utilities.
45    /// Many errors in special built-ins force the shell to exit.
46    Special,
47
48    /// Standard utility that can be used without `$PATH` search
49    ///
50    /// Mandatory built-ins are utilities that are listed in [POSIX XCU section
51    /// 1.7](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap01.html#tag_18_07).
52    /// In POSIX, they are called "intrinsic utilities".
53    ///
54    /// Like special built-ins, mandatory built-ins are not subject to `$PATH`
55    /// in command search; They are always found regardless of whether there is
56    /// a corresponding external utility in `$PATH`. However, mandatory
57    /// built-ins can still be overridden by functions.
58    ///
59    /// We call them "mandatory" because POSIX effectively requires them to be
60    /// built into the shell.
61    Mandatory,
62
63    /// Non-portable built-in that can be used without `$PATH` search
64    ///
65    /// Elective built-ins are built-ins that are listed in step 1b of [Command
66    /// Search and Execution](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_09_01_04)
67    /// in POSIX XCU section 2.9.1.4.
68    /// They are very similar to mandatory built-ins, but their behavior is not
69    /// specified by POSIX, so they are not portable. They cannot be used when
70    /// the (TODO TBD) option is set. <!-- An option that disables non-portable
71    /// behavior would make elective built-ins unusable even if found. An option
72    /// that disables non-conforming behavior would not affect elective
73    /// built-ins. -->
74    ///
75    /// We call them "elective" because it is up to the shell whether to
76    /// implement them.
77    Elective,
78
79    /// Non-conforming extension
80    ///
81    /// Extension built-ins are non-conformant extensions to the POSIX shell.
82    /// Like elective built-ins, they can be executed without `$PATH` search
83    /// finding a corresponding external utility. However, since this behavior
84    /// does not conform to [Command Search and
85    /// Execution](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_09_01_04)
86    /// in POSIX XCU section 2.9.1.4:
87    ///
88    /// - When the [`PosixlyCorrect`](crate::option::PosixlyCorrect) option is
89    ///   on, they are ignored: they are regarded as non-existing utilities so
90    ///   that the command search falls through to external utilities.
91    /// - When the (TODO TBD) option is on, they cannot be used even if found
92    ///   in command search.
93    Extension,
94
95    /// Built-in that works like a standalone utility
96    ///
97    /// A substitutive built-in is a built-in that is executed instead of an
98    /// external utility to minimize invocation overhead. Since a substitutive
99    /// built-in behaves just as if it were an external utility, it must be
100    /// found in `$PATH` in order to be executed.
101    Substitutive,
102}
103
104/// Result of built-in utility execution
105///
106/// The result type contains an exit status and optional flags that may affect
107/// the behavior of the shell following the built-in execution.
108#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
109#[must_use]
110pub struct Result {
111    exit_status: ExitStatus,
112    divert: crate::semantics::Result,
113    should_retain_redirs: bool,
114}
115
116impl Result {
117    /// Creates a new result.
118    pub const fn new(exit_status: ExitStatus) -> Self {
119        Self {
120            exit_status,
121            divert: crate::semantics::Result::Continue(()),
122            should_retain_redirs: false,
123        }
124    }
125
126    /// Creates a new result with a [`Divert`].
127    #[inline]
128    pub const fn with_exit_status_and_divert(
129        exit_status: ExitStatus,
130        divert: crate::semantics::Result,
131    ) -> Self {
132        Self {
133            exit_status,
134            divert,
135            should_retain_redirs: false,
136        }
137    }
138
139    /// Returns the exit status of this result.
140    ///
141    /// The return value is the argument to the previous invocation of
142    /// [`new`](Self::new) or [`set_exit_status`](Self::set_exit_status).
143    #[inline]
144    #[must_use]
145    pub const fn exit_status(&self) -> ExitStatus {
146        self.exit_status
147    }
148
149    /// Sets the exit status of this result.
150    ///
151    /// See [`exit_status`](Self::exit_status()).
152    #[inline]
153    pub fn set_exit_status(&mut self, exit_status: ExitStatus) {
154        self.exit_status = exit_status
155    }
156
157    /// Returns an optional [`Divert`] to be taken.
158    ///
159    /// The return value is the argument to the previous invocation of
160    /// [`set_divert`](Self::set_divert). The default is `Continue(())`.
161    #[inline]
162    pub const fn divert(&self) -> crate::semantics::Result {
163        self.divert
164    }
165
166    /// Sets a [`Divert`].
167    ///
168    /// See [`divert`](Self::divert()).
169    #[inline]
170    pub fn set_divert(&mut self, divert: crate::semantics::Result) {
171        self.divert = divert;
172    }
173
174    /// Tests whether the caller should retain redirections.
175    ///
176    /// Usually, the shell reverts redirections applied to a built-in after
177    /// executing it. However, redirections applied to a successful `exec`
178    /// built-in should persist. To make it happen, the `exec` built-in calls
179    /// [`retain_redirs`](Self::retain_redirs), and this function returns true.
180    /// In that case, the caller of the built-in should take appropriate actions
181    /// to preserve the effect of the redirections.
182    #[inline]
183    pub const fn should_retain_redirs(&self) -> bool {
184        self.should_retain_redirs
185    }
186
187    /// Flags that redirections applied to the built-in should persist.
188    ///
189    /// Calling this function makes
190    /// [`should_retain_redirs`](Self::should_retain_redirs) return true.
191    /// [`clear_redirs`](Self::clear_redirs) cancels the effect of this
192    /// function.
193    #[inline]
194    pub fn retain_redirs(&mut self) {
195        self.should_retain_redirs = true;
196    }
197
198    /// Cancels the effect of [`retain_redirs`](Self::retain_redirs).
199    #[inline]
200    pub fn clear_redirs(&mut self) {
201        self.should_retain_redirs = false;
202    }
203
204    /// Merges two results by taking the maximum of each field.
205    pub fn max(self, other: Self) -> Self {
206        use std::ops::ControlFlow::{Break, Continue};
207        let divert = match (self.divert, other.divert) {
208            (Continue(()), other) => other,
209            (other, Continue(())) => other,
210            (Break(left), Break(right)) => Break(left.max(right)),
211        };
212
213        Self {
214            exit_status: self.exit_status.max(other.exit_status),
215            divert,
216            should_retain_redirs: self.should_retain_redirs.max(other.should_retain_redirs),
217        }
218    }
219}
220
221impl Default for Result {
222    #[inline]
223    fn default() -> Self {
224        Self::new(ExitStatus::default())
225    }
226}
227
228impl From<ExitStatus> for Result {
229    #[inline]
230    fn from(exit_status: ExitStatus) -> Self {
231        Self::new(exit_status)
232    }
233}
234
235/// Type of functions that implement the behavior of a built-in
236///
237/// The function takes two arguments.
238/// The first is an environment in which the built-in is executed.
239/// The second is arguments to the built-in
240/// (not including the leading command name word).
241pub type Main<S> = fn(&mut Env<S>, Vec<Field>) -> Pin<Box<dyn Future<Output = Result> + '_>>;
242
243/// Built-in utility definition
244///
245/// # Notes on equality
246///
247/// Although this type implements `PartialEq`, comparison between instances of
248/// this type may not always yield predictable results due to the presence of
249/// function pointers. As a result, it is recommended to avoid relying on
250/// equality comparisons for values of this type. See
251/// <https://doc.rust-lang.org/std/ptr/fn.fn_addr_eq.html> for the
252/// characteristics of function pointer comparisons.
253#[allow(
254    unpredictable_function_pointer_comparisons,
255    reason = "we implement PartialEq for this type for historical reasons"
256)]
257#[non_exhaustive]
258pub struct Builtin<S> {
259    /// Type of the built-in
260    pub r#type: Type,
261
262    /// Function that implements the behavior of the built-in
263    pub execute: Main<S>,
264
265    /// Whether the built-in is a declaration utility
266    ///
267    /// The [`decl_util::Glossary`](crate::decl_util::Glossary) implementation
268    /// for [`Env`] uses this field to determine whether a command name is a
269    /// declaration utility. See the [method description] for the value this
270    /// field should have.
271    ///
272    /// [method description]: crate::decl_util::Glossary::is_declaration_utility
273    pub is_declaration_utility: Option<bool>,
274
275    /// Whether the built-in handles signals internally
276    ///
277    /// If `true`, the built-in is responsible for all signal handling during
278    /// its execution, including checking for caught signals and returning
279    /// `Break(Divert::Interrupt(...))` if appropriate. The caller must not
280    /// perform any additional signal checks while the built-in is executing.
281    ///
282    /// If `false` (the default), the built-in does not check for signals at
283    /// all. The caller then checks for a caught SIGINT concurrently with the
284    /// built-in's execution and interrupts the shell if needed. This allows the
285    /// shell to respond to SIGINT even for built-ins that never look at signals
286    /// themselves.
287    ///
288    /// Set this field to `true` for built-ins that handle signals themselves
289    /// (like `fg`, `wait`, `eval`, and `source`), to prevent double-processing.
290    pub handles_signals_internally: bool,
291}
292
293// Not derived automatically because S may not implement Clone or Copy.
294impl<S> Clone for Builtin<S> {
295    fn clone(&self) -> Self {
296        *self
297    }
298}
299
300impl<S> Copy for Builtin<S> {}
301
302impl<S> Debug for Builtin<S> {
303    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304        f.debug_struct("Builtin")
305            .field("type", &self.r#type)
306            .field("execute", &self.execute)
307            .field("is_declaration_utility", &self.is_declaration_utility)
308            .field(
309                "handles_signals_internally",
310                &self.handles_signals_internally,
311            )
312            .finish()
313    }
314}
315
316// Not derived automatically because S may not implement PartialEq, Eq, or Hash.
317impl<S> PartialEq for Builtin<S> {
318    fn eq(&self, other: &Self) -> bool {
319        self.r#type == other.r#type
320            && std::ptr::fn_addr_eq(self.execute, other.execute)
321            && self.is_declaration_utility == other.is_declaration_utility
322            && self.handles_signals_internally == other.handles_signals_internally
323    }
324}
325
326impl<S> Eq for Builtin<S> {}
327
328impl<S> std::hash::Hash for Builtin<S> {
329    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
330        self.r#type.hash(state);
331        self.execute.hash(state);
332        self.is_declaration_utility.hash(state);
333        self.handles_signals_internally.hash(state);
334    }
335}
336
337impl<S> Builtin<S> {
338    /// Creates a new built-in utility definition.
339    ///
340    /// The `type` and `execute` fields are set to the given arguments.
341    /// The `is_declaration_utility` field is set to `Some(false)`, indicating
342    /// that the built-in is not a declaration utility. The
343    /// `handles_signals_internally` field is set to `false`, meaning that
344    /// the built-in does not handle signals internally by default.
345    pub const fn new(r#type: Type, execute: Main<S>) -> Self {
346        Self {
347            r#type,
348            execute,
349            is_declaration_utility: Some(false),
350            handles_signals_internally: false,
351        }
352    }
353}