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
25#[cfg(doc)]
26use crate::semantics::Divert;
27use crate::semantics::ExitStatus;
28use crate::semantics::Field;
29use crate::Env;
30use std::fmt::Debug;
31use std::future::Future;
32use std::pin::Pin;
33
34pub mod getopts;
35
36/// Types of built-in utilities.
37#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
38pub enum Type {
39    /// Special built-in
40    ///
41    /// Special built-in utilities are built-ins that are defined in [POSIX XCU
42    /// section 2.14](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14).
43    ///
44    /// They are treated differently from other built-ins.
45    /// Especially, special built-ins are found in the first stage of command
46    /// search without the `$PATH` search and cannot be overridden by functions
47    /// or external utilities.
48    /// Many errors in special built-ins force the shell to exit.
49    Special,
50
51    /// Standard utility that can be used without `$PATH` search
52    ///
53    /// Mandatory built-ins are built-ins that are listed in step 1d of [Command
54    /// Search and Execution](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01)
55    /// in POSIX XCU section 2.9.1.
56    ///
57    /// Like special built-ins, mandatory built-ins are not subject to `$PATH`
58    /// in command search; They are always found regardless of whether there is
59    /// a corresponding external utility in `$PATH`. However, mandatory
60    /// built-ins can still be overridden by functions.
61    ///
62    /// We call them "mandatory" because POSIX effectively requires them to be
63    /// implemented by the shell.
64    Mandatory,
65
66    /// Non-portable built-in that can be used without `$PATH` search
67    ///
68    /// Elective built-ins are built-ins that are listed in step 1b of [Command
69    /// Search and Execution](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01)
70    /// in POSIX XCU section 2.9.1.
71    /// They are very similar to mandatory built-ins, but their behavior is not
72    /// specified by POSIX, so they are not portable. They cannot be used when
73    /// the (TODO TBD) option is set. <!-- An option that disables non-portable
74    /// behavior would make elective built-ins unusable even if found. An option
75    /// that disables non-conforming behavior would not affect elective
76    /// built-ins. -->
77    ///
78    /// We call them "elective" because it is up to the shell whether to
79    /// implement them.
80    Elective,
81
82    /// Non-portable extension
83    ///
84    /// Extension built-ins are non-conformant extensions to the POSIX shell.
85    /// Like elective built-ins, they can be executed without `$PATH` search
86    /// finding a corresponding external utility. However, since this behavior
87    /// does not conform to [Command
88    /// Search and Execution](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01)
89    /// in POSIX XCU section 2.9.1, they cannot be used when the (TODO TBD)
90    /// option is set. <!-- An option that disables non-conforming behavior
91    /// would make extension built-ins regarded as non-existing utilities. An
92    /// option that disables non-portable behavior would make extension
93    /// built-ins unusable even if found. -->
94    Extension,
95
96    /// Built-in that works as a standalone utility
97    ///
98    /// A substitutive built-in is a built-in that is executed instead of an
99    /// external utility to minimize invocation overhead. Since a substitutive
100    /// built-in behaves just as if it were an external utility, it must be
101    /// found in `$PATH` in order to be executed.
102    Substitutive,
103}
104
105/// Result of built-in utility execution.
106///
107/// The result type contains an exit status and optional flags that may affect
108/// the behavior of the shell following the built-in execution.
109#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
110#[must_use]
111pub struct Result {
112    exit_status: ExitStatus,
113    divert: crate::semantics::Result,
114    should_retain_redirs: bool,
115}
116
117impl Result {
118    /// Creates a new result.
119    pub const fn new(exit_status: ExitStatus) -> Self {
120        Self {
121            exit_status,
122            divert: crate::semantics::Result::Continue(()),
123            should_retain_redirs: false,
124        }
125    }
126
127    /// Creates a new result with a [`Divert`].
128    #[inline]
129    pub const fn with_exit_status_and_divert(
130        exit_status: ExitStatus,
131        divert: crate::semantics::Result,
132    ) -> Self {
133        Self {
134            exit_status,
135            divert,
136            should_retain_redirs: false,
137        }
138    }
139
140    /// Returns the exit status of this result.
141    ///
142    /// The return value is the argument to the previous invocation of
143    /// [`new`](Self::new) or [`set_exit_status`](Self::set_exit_status).
144    #[inline]
145    #[must_use]
146    pub const fn exit_status(&self) -> ExitStatus {
147        self.exit_status
148    }
149
150    /// Sets the exit status of this result.
151    ///
152    /// See [`exit_status`](Self::exit_status()).
153    #[inline]
154    pub fn set_exit_status(&mut self, exit_status: ExitStatus) {
155        self.exit_status = exit_status
156    }
157
158    /// Returns an optional [`Divert`] to be taken.
159    ///
160    /// The return value is the argument to the previous invocation of
161    /// [`set_divert`](Self::set_divert). The default is `Continue(())`.
162    #[inline]
163    #[must_use]
164    pub const fn divert(&self) -> crate::semantics::Result {
165        self.divert
166    }
167
168    /// Sets a [`Divert`].
169    ///
170    /// See [`divert`](Self::divert()).
171    #[inline]
172    pub fn set_divert(&mut self, divert: crate::semantics::Result) {
173        self.divert = divert;
174    }
175
176    /// Tests whether the caller should retain redirections.
177    ///
178    /// Usually, the shell reverts redirections applied to a built-in after
179    /// executing it. However, redirections applied to a successful `exec`
180    /// built-in should persist. To make it happen, the `exec` built-in calls
181    /// [`retain_redirs`](Self::retain_redirs), and this function returns true.
182    /// In that case, the caller of the built-in should take appropriate actions
183    /// to preserve the effect of the redirections.
184    #[inline]
185    pub const fn should_retain_redirs(&self) -> bool {
186        self.should_retain_redirs
187    }
188
189    /// Flags that redirections applied to the built-in should persist.
190    ///
191    /// Calling this function makes
192    /// [`should_retain_redirs`](Self::should_retain_redirs) return true.
193    /// [`clear_redirs`](Self::clear_redirs) cancels the effect of this
194    /// function.
195    #[inline]
196    pub fn retain_redirs(&mut self) {
197        self.should_retain_redirs = true;
198    }
199
200    /// Cancels the effect of [`retain_redirs`](Self::retain_redirs).
201    #[inline]
202    pub fn clear_redirs(&mut self) {
203        self.should_retain_redirs = false;
204    }
205
206    /// Merges two results by taking the maximum of each field.
207    pub fn max(self, other: Self) -> Self {
208        use std::ops::ControlFlow::{Break, Continue};
209        let divert = match (self.divert, other.divert) {
210            (Continue(()), other) => other,
211            (other, Continue(())) => other,
212            (Break(left), Break(right)) => Break(left.max(right)),
213        };
214
215        Self {
216            exit_status: self.exit_status.max(other.exit_status),
217            divert,
218            should_retain_redirs: self.should_retain_redirs.max(other.should_retain_redirs),
219        }
220    }
221}
222
223impl Default for Result {
224    #[inline]
225    fn default() -> Self {
226        Self::new(ExitStatus::default())
227    }
228}
229
230impl From<ExitStatus> for Result {
231    #[inline]
232    fn from(exit_status: ExitStatus) -> Self {
233        Self::new(exit_status)
234    }
235}
236
237/// Type of functions that implement the behavior of a built-in.
238///
239/// The function takes two arguments.
240/// The first is an environment in which the built-in is executed.
241/// The second is arguments to the built-in
242/// (not including the leading command name word).
243pub type Main = fn(&mut Env, Vec<Field>) -> Pin<Box<dyn Future<Output = Result> + '_>>;
244
245/// Built-in utility definition.
246#[derive(Clone, Copy, Eq, Hash, PartialEq)]
247pub struct Builtin {
248    /// Type of the built-in.
249    pub r#type: Type,
250    /// Function that implements the behavior of the built-in.
251    pub execute: Main,
252}
253
254impl Debug for Builtin {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        f.debug_struct("Builtin")
257            .field("type", &self.r#type)
258            .finish_non_exhaustive()
259    }
260}