yash-env 0.13.2

Yash shell execution environment interface
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
// This file is part of yash, an extended POSIX shell.
// Copyright (C) 2024 WATANABE Yuki
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Type definitions for signals
//!
//! Signals are a method of inter-process communication used to notify a process
//! that a specific event has occurred. This module provides types for
//! representing signals.
//!
//! At the OS API level, signals are represented simply as integers.
//! [`RawNumber`] is an alias for the integer type. To distinguish signals from
//! other integers, this module provides the [`Number`] type. Signal values and
//! their meanings vary by system. `Number` is designed to represent only the
//! values actually available on a given system.
//!
//! For clarity, signals are assigned names such as `SIGINT` and `SIGTERM`. To
//! convert between signal names and their numerical values, you can use the
//! constants and methods defined in the [`Signals`] trait.
//!
//! This module also provides an enum called [`Name`]. It is a compact value
//! for identifying signal names without using strings (`str`). This is an
//! somewhat obsolete type previously used as an intermediate representation for
//! converting between signal names and numbers. With the addition in `Signals`
//! of direct conversion methods between signal names and numbers, there remains
//! little practical need for using `Name`.
//!
//! All proper signal names start with `"SIG"`. However, functions that
//! operate on signal names usually expect names without the `"SIG"` prefix.

use crate::system::Errno;
#[cfg(doc)]
use crate::system::Signals;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::ffi::c_int;
use std::num::NonZero;
use std::str::FromStr;
use strum::{EnumIter, IntoEnumIterator};
use thiserror::Error;

/// Raw signal number
///
/// This is a type alias for the raw signal number used by the underlying
/// system. POSIX requires valid signal numbers to be positive `c_int` values.
pub type RawNumber = c_int;

/// Signal name
///
/// This enum identifies a signal by its name. It can be used to represent
/// signals regardless of whether they are available on a specific system
/// implementing the [`Signals`] trait.
///
/// Use the [`Signals::validate_signal`] function to obtain a `Name` from a
/// signal number. The [`Signals::signal_number_from_name`] function can be used
/// to convert a `Name` to a `Number`.
///
/// This type is soft-deprecated. In most cases, you should use ordinary
/// strings to represent signal names, and use the conversion methods in
/// [`Signals`] to convert between names and numbers.
#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Name {
    /// `SIGABRT` (process abort signal)
    Abrt,
    /// `SIGALRM` (alarm clock)
    Alrm,
    /// `SIGBUS` (access to an undefined portion of a memory object)
    Bus,
    /// `SIGCHLD` (child process terminated, stopped, or continued)
    Chld,
    /// `SIGCLD` (child process terminated, stopped, or continued)
    Cld,
    /// `SIGCONT` (continue executing, if stopped)
    Cont,
    /// `SIGEMT` (emulation trap)
    Emt,
    /// `SIGFPE` (erroneous arithmetic operation)
    Fpe,
    /// `SIGHUP` (hangup)
    Hup,
    /// `SIGILL` (illegal instruction)
    Ill,
    /// `SIGINFO` (status request from keyboard)
    Info,
    /// `SIGINT` (interrupt)
    Int,
    /// `SIGIO` (I/O is possible on a file descriptor)
    Io,
    /// `SIGIOT` (I/O trap)
    Iot,
    /// `SIGKILL` (kill)
    Kill,
    /// `SIGLOST` (resource lost)
    Lost,
    /// `SIGPIPE` (write on a pipe with no one to read it)
    Pipe,
    /// `SIGPOLL` (pollable event)
    Poll,
    /// `SIGPROF` (profiling timer expired)
    Prof,
    /// `SIGPWR` (power failure)
    Pwr,
    /// `SIGQUIT` (quit)
    Quit,
    /// `SIGSEGV` (invalid memory reference)
    Segv,
    /// `SIGSTKFLT` (stack fault)
    Stkflt,
    /// `SIGSTOP` (stop executing)
    Stop,
    /// `SIGSYS` (bad system call)
    Sys,
    /// `SIGTERM` (termination)
    Term,
    /// `SIGTHR` (thread interrupt)
    Thr,
    /// `SIGTRAP` (trace trap)
    Trap,
    /// `SIGTSTP` (stop executing)
    Tstp,
    /// `SIGTTIN` (background process attempting read)
    Ttin,
    /// `SIGTTOU` (background process attempting write)
    Ttou,
    /// `SIGURG` (high bandwidth data is available at a socket)
    Urg,
    /// `SIGUSR1` (user-defined signal 1)
    Usr1,
    /// `SIGUSR2` (user-defined signal 2)
    Usr2,
    /// `SIGVTALRM` (virtual timer expired)
    Vtalrm,
    /// `SIGWINCH` (window size change)
    Winch,
    /// `SIGXCPU` (CPU time limit exceeded)
    Xcpu,
    /// `SIGXFSZ` (file size limit exceeded)
    Xfsz,

    /// Real-time signal with a number relative to the minimum real-time signal
    ///
    /// `Rtmin(n)` represents the real-time signal `SIGRTMIN + n`, where `n` is
    /// expected to be a non-negative integer between `0` and
    /// `SIGRTMAX - SIGRTMIN`.
    Rtmin(RawNumber),

    /// Real-time signal with a number relative to the maximum real-time signal
    ///
    /// `Rtmax(n)` represents the real-time signal `SIGRTMAX + n`, where `n` is
    /// expected to be a non-positive integer between `SIGRTMIN - SIGRTMAX` and
    /// `0`.
    Rtmax(RawNumber),
}

/// Compares two signal names.
///
/// The comparison is allowed only between two `Rtmin` values or two `Rtmax`
/// values. The comparison is based on the numerical value of the signal number.
impl PartialOrd for Name {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match (self, other) {
            (Self::Rtmin(a), Self::Rtmin(b)) | (Self::Rtmax(a), Self::Rtmax(b)) => a.partial_cmp(b),
            _ => None,
        }
    }
}

impl Name {
    /// Returns an iterator over all signal names.
    ///
    /// This is a convenience method that returns an iterator over all signal
    /// names. The iterator yields all signal names other than `Rtmin` and
    /// `Rtmax` in the alphabetical order, followed by `Rtmin(0)` and `Rtmax(0)`
    /// as the last two items.
    ///
    /// Note that the iterator works independently of the underlying system and
    /// does not check whether the signals are available on the system.
    #[inline(always)]
    pub fn iter() -> NameIter {
        <Self as IntoEnumIterator>::iter()
    }

    /// Returns the name as a string.
    ///
    /// For most signals, this function returns a static string that is the
    /// signal name in uppercase without the `"SIG"` prefix. For real-time
    /// signals `Rtmin(n)` and `Rtmax(n)` where `n` is non-zero, this function
    /// returns a dynamically allocated string that is `RTMIN` or `RTMAX`
    /// followed by the relative number `n`. Examples of the returned strings
    /// are `"TERM"`, `"RTMIN"`, and `"RTMAX-5"`.
    ///
    /// The returned name can be converted back to the signal using the
    /// [`FromStr`] implementation for `Name`.
    #[must_use]
    pub fn as_string(&self) -> Cow<'static, str> {
        match *self {
            Self::Abrt => Cow::Borrowed("ABRT"),
            Self::Alrm => Cow::Borrowed("ALRM"),
            Self::Bus => Cow::Borrowed("BUS"),
            Self::Chld => Cow::Borrowed("CHLD"),
            Self::Cld => Cow::Borrowed("CLD"),
            Self::Cont => Cow::Borrowed("CONT"),
            Self::Emt => Cow::Borrowed("EMT"),
            Self::Fpe => Cow::Borrowed("FPE"),
            Self::Hup => Cow::Borrowed("HUP"),
            Self::Ill => Cow::Borrowed("ILL"),
            Self::Info => Cow::Borrowed("INFO"),
            Self::Int => Cow::Borrowed("INT"),
            Self::Io => Cow::Borrowed("IO"),
            Self::Iot => Cow::Borrowed("IOT"),
            Self::Kill => Cow::Borrowed("KILL"),
            Self::Lost => Cow::Borrowed("LOST"),
            Self::Pipe => Cow::Borrowed("PIPE"),
            Self::Poll => Cow::Borrowed("POLL"),
            Self::Prof => Cow::Borrowed("PROF"),
            Self::Pwr => Cow::Borrowed("PWR"),
            Self::Quit => Cow::Borrowed("QUIT"),
            Self::Segv => Cow::Borrowed("SEGV"),
            Self::Stkflt => Cow::Borrowed("STKFLT"),
            Self::Stop => Cow::Borrowed("STOP"),
            Self::Sys => Cow::Borrowed("SYS"),
            Self::Term => Cow::Borrowed("TERM"),
            Self::Thr => Cow::Borrowed("THR"),
            Self::Trap => Cow::Borrowed("TRAP"),
            Self::Tstp => Cow::Borrowed("TSTP"),
            Self::Ttin => Cow::Borrowed("TTIN"),
            Self::Ttou => Cow::Borrowed("TTOU"),
            Self::Urg => Cow::Borrowed("URG"),
            Self::Usr1 => Cow::Borrowed("USR1"),
            Self::Usr2 => Cow::Borrowed("USR2"),
            Self::Vtalrm => Cow::Borrowed("VTALRM"),
            Self::Winch => Cow::Borrowed("WINCH"),
            Self::Xcpu => Cow::Borrowed("XCPU"),
            Self::Xfsz => Cow::Borrowed("XFSZ"),
            Self::Rtmin(0) => Cow::Borrowed("RTMIN"),
            Self::Rtmax(0) => Cow::Borrowed("RTMAX"),
            Self::Rtmin(n) => Cow::Owned(format!("RTMIN{n:+}")),
            Self::Rtmax(n) => Cow::Owned(format!("RTMAX{n:+}")),
        }
    }
}

impl std::fmt::Display for Name {
    /// Writes the signal name to the formatter.
    ///
    /// See [`Name::as_string`] for the format of the produced string.
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.as_string().fmt(f)
    }
}

#[test]
fn test_name_to_string() {
    assert_eq!(Name::Term.to_string(), "TERM");
    assert_eq!(Name::Int.to_string(), "INT");
    assert_eq!(Name::Rtmin(0).to_string(), "RTMIN");
    assert_eq!(Name::Rtmax(0).to_string(), "RTMAX");
    assert_eq!(Name::Rtmin(1).to_string(), "RTMIN+1");
    assert_eq!(Name::Rtmin(20).to_string(), "RTMIN+20");
    assert_eq!(Name::Rtmax(-1).to_string(), "RTMAX-1");
    assert_eq!(Name::Rtmax(-20).to_string(), "RTMAX-20");
}

/// Error value for an unknown signal name
///
/// This error is used by the [`FromStr`] implementation for [`Name`] to
/// indicate that the input string does not match any known signal name.
#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
pub struct UnknownNameError;

impl std::fmt::Display for UnknownNameError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("unknown signal name")
    }
}

/// Converts an unknown signal name error to `Errno::EINVAL`.
impl From<UnknownNameError> for Errno {
    #[inline]
    fn from(UnknownNameError: UnknownNameError) -> Self {
        Errno::EINVAL
    }
}

/// Parses a signal name from a string.
///
/// This function parses a signal name from a string. The input string is
/// expected to be an uppercase signal name without the `"SIG"` prefix. The
/// function returns the corresponding signal name if the input string matches a
/// known signal name. Otherwise, it returns an error.
///
/// See [`Name::as_string`] for the format of the input string.
impl FromStr for Name {
    type Err = UnknownNameError;

    fn from_str(s: &str) -> Result<Self, UnknownNameError> {
        match s {
            "ABRT" => Ok(Self::Abrt),
            "ALRM" => Ok(Self::Alrm),
            "BUS" => Ok(Self::Bus),
            "CHLD" => Ok(Self::Chld),
            "CLD" => Ok(Self::Cld),
            "CONT" => Ok(Self::Cont),
            "EMT" => Ok(Self::Emt),
            "FPE" => Ok(Self::Fpe),
            "HUP" => Ok(Self::Hup),
            "ILL" => Ok(Self::Ill),
            "INFO" => Ok(Self::Info),
            "INT" => Ok(Self::Int),
            "IO" => Ok(Self::Io),
            "IOT" => Ok(Self::Iot),
            "KILL" => Ok(Self::Kill),
            "LOST" => Ok(Self::Lost),
            "PIPE" => Ok(Self::Pipe),
            "POLL" => Ok(Self::Poll),
            "PROF" => Ok(Self::Prof),
            "PWR" => Ok(Self::Pwr),
            "QUIT" => Ok(Self::Quit),
            "SEGV" => Ok(Self::Segv),
            "STKFLT" => Ok(Self::Stkflt),
            "STOP" => Ok(Self::Stop),
            "SYS" => Ok(Self::Sys),
            "TERM" => Ok(Self::Term),
            "THR" => Ok(Self::Thr),
            "TRAP" => Ok(Self::Trap),
            "TSTP" => Ok(Self::Tstp),
            "TTIN" => Ok(Self::Ttin),
            "TTOU" => Ok(Self::Ttou),
            "URG" => Ok(Self::Urg),
            "USR1" => Ok(Self::Usr1),
            "USR2" => Ok(Self::Usr2),
            "VTALRM" => Ok(Self::Vtalrm),
            "WINCH" => Ok(Self::Winch),
            "XCPU" => Ok(Self::Xcpu),
            "XFSZ" => Ok(Self::Xfsz),

            "RTMIN" => Ok(Self::Rtmin(0)),
            "RTMAX" => Ok(Self::Rtmax(0)),
            _ => {
                if let Some(tail) = s.strip_prefix("RTMIN") {
                    if tail.starts_with(['+', '-']) {
                        if let Ok(n) = tail.parse() {
                            return Ok(Self::Rtmin(n));
                        }
                    }
                }
                if let Some(tail) = s.strip_prefix("RTMAX") {
                    if tail.starts_with(['+', '-']) {
                        if let Ok(n) = tail.parse() {
                            return Ok(Self::Rtmax(n));
                        }
                    }
                }
                Err(UnknownNameError)
            }
        }
    }
}

#[test]
fn test_name_from_str() {
    assert_eq!("ABRT".parse(), Ok(Name::Abrt));
    assert_eq!("INT".parse(), Ok(Name::Int));
    assert_eq!("QUIT".parse(), Ok(Name::Quit));

    assert_eq!("RTMIN".parse(), Ok(Name::Rtmin(0)));
    assert_eq!("RTMIN+0".parse(), Ok(Name::Rtmin(0)));
    assert_eq!("RTMIN+1".parse(), Ok(Name::Rtmin(1)));

    assert_eq!("RTMAX".parse(), Ok(Name::Rtmax(0)));
    assert_eq!("RTMAX-0".parse(), Ok(Name::Rtmax(0)));
    assert_eq!("RTMAX-1".parse(), Ok(Name::Rtmax(-1)));

    assert_eq!("".parse::<Name>(), Err(UnknownNameError));
    assert_eq!("FOO".parse::<Name>(), Err(UnknownNameError));
    assert_eq!("int".parse::<Name>(), Err(UnknownNameError));
    assert_eq!("RTMIN0".parse::<Name>(), Err(UnknownNameError));
    assert_eq!("RTMIN+".parse::<Name>(), Err(UnknownNameError));
    assert_eq!("RTMAX0".parse::<Name>(), Err(UnknownNameError));
    assert_eq!("RTMAX-".parse::<Name>(), Err(UnknownNameError));
    assert_eq!("2".parse::<Name>(), Err(UnknownNameError));
}

/// Signal number
///
/// This is a wrapper type for signal numbers. POSIX requires valid signal
/// numbers to be positive `c_int` values. Therefore, this type wraps a
/// `NonZero<RawNumber>`, guaranteeing that the signal number is always
/// non-zero.
///
/// To make sure that all `Number`s are valid, you should only obtain them
/// from an instance of [`Signals`], which provides constants and methods to
/// convert between signal names and numbers.
///
/// Signal numbers are specific to the underlying system. Passing a signal
/// number obtained from [`RealSystem`] to [`VirtualSystem`] (or vice versa) is
/// not supported and may result in unexpected behavior, though it is not
/// checked by the type system.
///
/// [`RealSystem`]: crate::system::real::RealSystem
/// [`VirtualSystem`]: crate::system::virtual::VirtualSystem
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
#[repr(transparent)]
pub struct Number(NonZero<RawNumber>);

impl Number {
    /// Returns the raw signal number.
    #[inline(always)]
    #[must_use]
    pub const fn as_raw(self) -> RawNumber {
        self.0.get()
    }

    /// Returns the raw signal number as a `NonZero<RawNumber>`.
    #[inline(always)]
    #[must_use]
    pub const fn as_raw_non_zero(self) -> NonZero<RawNumber> {
        self.0
    }

    /// Creates a new `Number` from a raw signal number.
    ///
    /// This is a backdoor method that allows creating a `Number` from an
    /// arbitrary non-zero integer. The caller must ensure that the raw signal
    /// number is a valid signal number.
    ///
    /// This function is not marked `unsafe` because creating an invalid
    /// `Number` does not lead to undefined behavior. However, it is not
    /// recommended to use this function unless you are sure that the raw signal
    /// number is valid. In most cases, you should obtain `Number`s from an
    /// instance of [`Signals`].
    #[inline(always)]
    #[must_use]
    pub const fn from_raw_unchecked(raw: NonZero<RawNumber>) -> Self {
        Self(raw)
    }
}

impl From<Number> for NonZero<RawNumber> {
    #[inline(always)]
    fn from(number: Number) -> Self {
        number.0
    }
}

impl From<Number> for RawNumber {
    #[inline(always)]
    fn from(number: Number) -> Self {
        number.0.get()
    }
}

impl std::fmt::Display for Number {
    #[inline(always)]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}
impl std::fmt::Binary for Number {
    #[inline(always)]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}
impl std::fmt::Octal for Number {
    #[inline(always)]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}
impl std::fmt::LowerHex for Number {
    #[inline(always)]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}
impl std::fmt::UpperHex for Number {
    #[inline(always)]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}