Skip to content

Commit

Permalink
share code with trap builtin
Browse files Browse the repository at this point in the history
  • Loading branch information
39555 committed Oct 22, 2024
1 parent 03c0a4e commit 0407a0a
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 134 deletions.
79 changes: 29 additions & 50 deletions brush-core/src/builtins/kill.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use clap::Parser;
use nix::sys::signal::Signal;
use std::io::Write;
use std::str::FromStr;

use crate::traps::{self, TrapSignal};
use crate::{builtins, commands, error};

/// Signal a job or process.
Expand Down Expand Up @@ -77,65 +77,44 @@ fn print_signals(
signals: &[String],
) -> Result<builtins::ExitCode, error::Error> {
let mut exit_code = builtins::ExitCode::Success;
// TODO: `0 EXIT` signal is missing. It is not in the posix spec, but it exists in Bash
// https://man7.org/linux/man-pages/man7/signal.7.html
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
if !signals.is_empty() {
for s in signals {
// If the user gives us a code, we print the name; if they give a name, we print its
// code.
enum Sigspec {
Sigspec(&'static str),
Signum(i32),
enum PrintSignal {
Name(&'static str),
Num(i32),
}
let signal = s
.parse::<i32>()
.ok()
.and_then(|code| {
Signal::try_from(code)
.map(|s| {
// bash compatinility. `SIGHUP` -> `HUP`
Sigspec::Sigspec(s.as_str().strip_prefix("SIG").unwrap_or(s.as_str()))
})
.ok()

let signal = if let Ok(n) = s.parse::<i32>() {
// bash compatibility. `SIGHUP` -> `HUP`
TrapSignal::try_from(n).map(|s| {
PrintSignal::Name(s.as_str().strip_prefix("SIG").unwrap_or(s.as_str()))
})
.or_else(|| {
// bash compatibility:
// support for names without `SIG`, for example `HUP` -> `SIGHUP`
let mut sig_str = String::with_capacity(3 + s.len());
if s.len() >= 3 && s[..3] != *"SIG" {
sig_str.push_str("SIG");
sig_str.push_str(s.as_str());
} else {
sig_str.push_str(s.as_str());
}
Signal::from_str(sig_str.as_str())
.ok()
.map(|s| Sigspec::Signum(s as i32))
});
if let Some(signal) = signal {
match signal {
Sigspec::Signum(n) => {
writeln!(context.stdout(), "{n}")?;
}
Sigspec::Sigspec(s) => {
writeln!(context.stdout(), "{s}")?;
}
}
} else {
writeln!(
context.stderr(),
"{}: {}: invalid signal specification",
context.command_name,
s
)?;
exit_code = builtins::ExitCode::Custom(1);
TrapSignal::from_str(s).map(|s| PrintSignal::Num(i32::from(s)))
};

match signal {
Ok(PrintSignal::Num(n)) => {
writeln!(context.stdout(), "{n}")?;
}
Ok(PrintSignal::Name(s)) => {
writeln!(context.stdout(), "{s}")?;
}
Err(e) => {
writeln!(context.stderr(), "{e}")?;
exit_code = builtins::ExitCode::Custom(1);
}
}
}
} else {
for i in Signal::iterator() {
writeln!(context.stdout(), "{i}")?;
}
return traps::format_signals(
context.stdout(),
TrapSignal::iterator()
.filter(|s| !matches!(s, TrapSignal::Err | TrapSignal::Debug | TrapSignal::Exit)),
)
.map(|()| builtins::ExitCode::Success);
}

Ok(exit_code)
Expand Down
54 changes: 10 additions & 44 deletions brush-core/src/builtins/trap.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use clap::Parser;
use std::io::Write;

use crate::{builtins, commands, error, sys, traps};
use crate::traps::TrapSignal;
use crate::{builtins, commands, error};

/// Manage signal traps.
#[derive(Parser)]
Expand All @@ -23,29 +24,27 @@ impl builtins::Command for TrapCommand {
mut context: commands::ExecutionContext<'_>,
) -> Result<builtins::ExitCode, crate::error::Error> {
if self.list_signals {
Self::display_signals(&context)?;
Ok(builtins::ExitCode::Success)
crate::traps::format_signals(context.stdout(), TrapSignal::iterator())
.map(|()| builtins::ExitCode::Success)
} else if self.print_trap_commands || self.args.is_empty() {
if !self.args.is_empty() {
for signal_type in &self.args {
let signal_type = parse_signal(signal_type)?;
Self::display_handlers_for(&context, signal_type)?;
Self::display_handlers_for(&context, signal_type.parse()?)?;
}
} else {
Self::display_all_handlers(&context)?;
}
Ok(builtins::ExitCode::Success)
} else if self.args.len() == 1 {
let signal = self.args[0].as_str();
let signal_type = parse_signal(signal)?;
Self::remove_all_handlers(&mut context, signal_type);
Self::remove_all_handlers(&mut context, signal.parse()?);
Ok(builtins::ExitCode::Success)
} else {
let handler = &self.args[0];

let mut signal_types = vec![];
for signal in &self.args[1..] {
signal_types.push(parse_signal(signal)?);
signal_types.push(signal.parse()?);
}

Self::register_handler(&mut context, signal_types, handler.as_str());
Expand All @@ -56,16 +55,6 @@ impl builtins::Command for TrapCommand {

#[allow(unused_variables)]
impl TrapCommand {
#[allow(clippy::unnecessary_wraps)]
fn display_signals(context: &commands::ExecutionContext<'_>) -> Result<(), error::Error> {
#[cfg(unix)]
for signal in nix::sys::signal::Signal::iterator() {
writeln!(context.stdout(), "{}: {signal}", signal as i32)?;
}

Ok(())
}

fn display_all_handlers(context: &commands::ExecutionContext<'_>) -> Result<(), error::Error> {
for signal in context.shell.traps.handlers.keys() {
Self::display_handlers_for(context, *signal)?;
Expand All @@ -75,7 +64,7 @@ impl TrapCommand {

fn display_handlers_for(
context: &commands::ExecutionContext<'_>,
signal_type: traps::TrapSignal,
signal_type: TrapSignal,
) -> Result<(), error::Error> {
if let Some(handler) = context.shell.traps.handlers.get(&signal_type) {
writeln!(context.stdout(), "trap -- '{handler}' {signal_type}")?;
Expand All @@ -85,14 +74,14 @@ impl TrapCommand {

fn remove_all_handlers(
context: &mut crate::commands::ExecutionContext<'_>,
signal: traps::TrapSignal,
signal: TrapSignal,
) {
context.shell.traps.remove_handlers(signal);
}

fn register_handler(
context: &mut crate::commands::ExecutionContext<'_>,
signals: Vec<traps::TrapSignal>,
signals: Vec<TrapSignal>,
handler: &str,
) {
for signal in signals {
Expand All @@ -103,26 +92,3 @@ impl TrapCommand {
}
}
}

fn parse_signal(signal: &str) -> Result<traps::TrapSignal, error::Error> {
if signal.chars().all(|c| c.is_ascii_digit()) {
let digits = signal
.parse::<i32>()
.map_err(|_| error::Error::InvalidSignal)?;

sys::signal::parse_numeric_signal(digits)
} else {
let mut signal_to_parse = signal.to_ascii_uppercase();

if !signal_to_parse.starts_with("SIG") {
signal_to_parse.insert_str(0, "SIG");
}

match signal_to_parse {
s if s == "SIGDEBUG" => Ok(traps::TrapSignal::Debug),
s if s == "SIGERR" => Ok(traps::TrapSignal::Err),
s if s == "SIGEXIT" => Ok(traps::TrapSignal::Exit),
s => sys::signal::parse_os_signal_name(s.as_str()),
}
}
}
2 changes: 1 addition & 1 deletion brush-core/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ impl Spec {
}
}
CompleteAction::Signal => {
for signal in traps::TrapSignal::all_values() {
for signal in traps::TrapSignal::iterator() {
candidates.insert(signal.to_string());
}
}
Expand Down
4 changes: 2 additions & 2 deletions brush-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ pub enum Error {
ThreadingError(#[from] tokio::task::JoinError),

/// An invalid signal was referenced.
#[error("invalid signal")]
InvalidSignal,
#[error("{0}: invalid signal specification")]
InvalidSignal(String),

/// A system error occurred.
#[cfg(unix)]
Expand Down
8 changes: 0 additions & 8 deletions brush-core/src/sys/stubs/signal.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
use crate::{error, sys, traps};

pub(crate) fn parse_numeric_signal(_signal: i32) -> Result<traps::TrapSignal, error::Error> {
Err(error::Error::InvalidSignal)
}

pub(crate) fn parse_os_signal_name(_signal: &str) -> Result<traps::TrapSignal, error::Error> {
Err(error::Error::InvalidSignal)
}

pub(crate) fn continue_process(_pid: sys::process::ProcessId) -> Result<(), error::Error> {
error::unimp("continue process")
}
Expand Down
16 changes: 1 addition & 15 deletions brush-core/src/sys/unix/signal.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
use std::str::FromStr;

use crate::{error, sys, traps};

pub(crate) fn parse_numeric_signal(signal: i32) -> Result<traps::TrapSignal, error::Error> {
Ok(traps::TrapSignal::Signal(
nix::sys::signal::Signal::try_from(signal).map_err(|_| error::Error::InvalidSignal)?,
))
}

pub(crate) fn parse_os_signal_name(signal: &str) -> Result<traps::TrapSignal, error::Error> {
Ok(traps::TrapSignal::Signal(
nix::sys::signal::Signal::from_str(signal).map_err(|_| error::Error::InvalidSignal)?,
))
}
use crate::{error, sys};

pub(crate) fn continue_process(pid: sys::process::ProcessId) -> Result<(), error::Error> {
#[allow(clippy::cast_possible_wrap)]
Expand Down
Loading

0 comments on commit 0407a0a

Please sign in to comment.