command arguments are now underlined if they match an existing path -m ran rustfmt on the entire codebase
This commit is contained in:
@@ -3,7 +3,7 @@ use crate::{
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
parse::{NdRule, Node},
|
||||
prelude::*,
|
||||
procio::{borrow_fd, IoStack},
|
||||
procio::{IoStack, borrow_fd},
|
||||
state::{self, read_logic, write_logic},
|
||||
};
|
||||
|
||||
|
||||
@@ -43,15 +43,19 @@ pub fn cd(node: Node, job: &mut JobBldr) -> ShResult<()> {
|
||||
}
|
||||
|
||||
if let Err(e) = env::set_current_dir(new_dir) {
|
||||
return Err(ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
format!("cd: Failed to change directory: {}", e),
|
||||
span,
|
||||
));
|
||||
}
|
||||
let new_dir = env::current_dir().map_err(
|
||||
|e| ShErr::full(ShErrKind::ExecFail, format!("cd: Failed to get current directory: {}", e), span)
|
||||
)?;
|
||||
return Err(ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
format!("cd: Failed to change directory: {}", e),
|
||||
span,
|
||||
));
|
||||
}
|
||||
let new_dir = env::current_dir().map_err(|e| {
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
format!("cd: Failed to get current directory: {}", e),
|
||||
span,
|
||||
)
|
||||
})?;
|
||||
unsafe { env::set_var("PWD", new_dir) };
|
||||
|
||||
state::set_status(0);
|
||||
|
||||
@@ -1,14 +1,34 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::{
|
||||
builtin::setup_builtin, expand::expand_prompt, getopt::{Opt, OptSpec, get_opts_from_tokens}, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult, ShResultExt}, parse::{NdRule, Node}, prelude::*, procio::{IoStack, borrow_fd}, state
|
||||
builtin::setup_builtin,
|
||||
expand::expand_prompt,
|
||||
getopt::{Opt, OptSpec, get_opts_from_tokens},
|
||||
jobs::JobBldr,
|
||||
libsh::error::{ShErr, ShErrKind, ShResult, ShResultExt},
|
||||
parse::{NdRule, Node},
|
||||
prelude::*,
|
||||
procio::{IoStack, borrow_fd},
|
||||
state,
|
||||
};
|
||||
|
||||
pub const ECHO_OPTS: [OptSpec;4] = [
|
||||
OptSpec { opt: Opt::Short('n'), takes_arg: false },
|
||||
OptSpec { opt: Opt::Short('E'), takes_arg: false },
|
||||
OptSpec { opt: Opt::Short('e'), takes_arg: false },
|
||||
OptSpec { opt: Opt::Short('p'), takes_arg: false },
|
||||
pub const ECHO_OPTS: [OptSpec; 4] = [
|
||||
OptSpec {
|
||||
opt: Opt::Short('n'),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Short('E'),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Short('e'),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Short('p'),
|
||||
takes_arg: false,
|
||||
},
|
||||
];
|
||||
|
||||
bitflags! {
|
||||
@@ -16,7 +36,7 @@ bitflags! {
|
||||
const NO_NEWLINE = 0b000001;
|
||||
const USE_STDERR = 0b000010;
|
||||
const USE_ESCAPE = 0b000100;
|
||||
const USE_PROMPT = 0b001000;
|
||||
const USE_PROMPT = 0b001000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,13 +60,15 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
borrow_fd(STDOUT_FILENO)
|
||||
};
|
||||
|
||||
let mut echo_output = prepare_echo_args(argv
|
||||
.into_iter()
|
||||
.map(|a| a.0) // Extract the String from the tuple of (String,Span)
|
||||
.collect::<Vec<_>>(),
|
||||
flags.contains(EchoFlags::USE_ESCAPE),
|
||||
flags.contains(EchoFlags::USE_PROMPT)
|
||||
)?.join(" ");
|
||||
let mut echo_output = prepare_echo_args(
|
||||
argv
|
||||
.into_iter()
|
||||
.map(|a| a.0) // Extract the String from the tuple of (String,Span)
|
||||
.collect::<Vec<_>>(),
|
||||
flags.contains(EchoFlags::USE_ESCAPE),
|
||||
flags.contains(EchoFlags::USE_PROMPT),
|
||||
)?
|
||||
.join(" ");
|
||||
|
||||
if !flags.contains(EchoFlags::NO_NEWLINE) && !echo_output.ends_with('\n') {
|
||||
echo_output.push('\n')
|
||||
@@ -58,137 +80,141 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn prepare_echo_args(argv: Vec<String>, use_escape: bool, use_prompt: bool) -> ShResult<Vec<String>> {
|
||||
if !use_escape {
|
||||
if use_prompt {
|
||||
let expanded: ShResult<Vec<String>> = argv
|
||||
.into_iter()
|
||||
.map(|s| expand_prompt(s.as_str()))
|
||||
.collect();
|
||||
return expanded
|
||||
}
|
||||
return Ok(argv);
|
||||
}
|
||||
pub fn prepare_echo_args(
|
||||
argv: Vec<String>,
|
||||
use_escape: bool,
|
||||
use_prompt: bool,
|
||||
) -> ShResult<Vec<String>> {
|
||||
if !use_escape {
|
||||
if use_prompt {
|
||||
let expanded: ShResult<Vec<String>> = argv
|
||||
.into_iter()
|
||||
.map(|s| expand_prompt(s.as_str()))
|
||||
.collect();
|
||||
return expanded;
|
||||
}
|
||||
return Ok(argv);
|
||||
}
|
||||
|
||||
let mut prepared_args = Vec::with_capacity(argv.len());
|
||||
let mut prepared_args = Vec::with_capacity(argv.len());
|
||||
|
||||
for arg in argv {
|
||||
let mut prepared_arg = String::new();
|
||||
if use_prompt {
|
||||
prepared_arg = expand_prompt(&prepared_arg)?;
|
||||
}
|
||||
for arg in argv {
|
||||
let mut prepared_arg = String::new();
|
||||
if use_prompt {
|
||||
prepared_arg = expand_prompt(&prepared_arg)?;
|
||||
}
|
||||
|
||||
let mut chars = arg.chars().peekable();
|
||||
let mut chars = arg.chars().peekable();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '\\' {
|
||||
if let Some(&next_char) = chars.peek() {
|
||||
match next_char {
|
||||
'n' => {
|
||||
prepared_arg.push('\n');
|
||||
chars.next();
|
||||
}
|
||||
't' => {
|
||||
prepared_arg.push('\t');
|
||||
chars.next();
|
||||
}
|
||||
'r' => {
|
||||
prepared_arg.push('\r');
|
||||
chars.next();
|
||||
}
|
||||
'a' => {
|
||||
prepared_arg.push('\x07');
|
||||
chars.next();
|
||||
}
|
||||
'b' => {
|
||||
prepared_arg.push('\x08');
|
||||
chars.next();
|
||||
}
|
||||
'e' | 'E' => {
|
||||
prepared_arg.push('\x1b');
|
||||
chars.next();
|
||||
}
|
||||
'x' => {
|
||||
chars.next(); // consume 'x'
|
||||
let mut hex_digits = String::new();
|
||||
for _ in 0..2 {
|
||||
if let Some(&hex_char) = chars.peek() {
|
||||
if hex_char.is_ascii_hexdigit() {
|
||||
hex_digits.push(hex_char);
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Ok(value) = u8::from_str_radix(&hex_digits, 16) {
|
||||
prepared_arg.push(value as char);
|
||||
} else {
|
||||
prepared_arg.push('\\');
|
||||
prepared_arg.push('x');
|
||||
prepared_arg.push_str(&hex_digits);
|
||||
}
|
||||
}
|
||||
'0' => {
|
||||
chars.next(); // consume '0'
|
||||
let mut octal_digits = String::new();
|
||||
for _ in 0..3 {
|
||||
if let Some(&octal_char) = chars.peek() {
|
||||
if ('0'..='7').contains(&octal_char) {
|
||||
octal_digits.push(octal_char);
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Ok(value) = u8::from_str_radix(&octal_digits, 8) {
|
||||
prepared_arg.push(value as char);
|
||||
} else {
|
||||
prepared_arg.push('\\');
|
||||
prepared_arg.push('0');
|
||||
prepared_arg.push_str(&octal_digits);
|
||||
}
|
||||
}
|
||||
'\\' => {
|
||||
prepared_arg.push('\\');
|
||||
chars.next();
|
||||
}
|
||||
_ => prepared_arg.push(c),
|
||||
}
|
||||
} else {
|
||||
prepared_arg.push(c);
|
||||
}
|
||||
} else {
|
||||
prepared_arg.push(c);
|
||||
}
|
||||
}
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '\\' {
|
||||
if let Some(&next_char) = chars.peek() {
|
||||
match next_char {
|
||||
'n' => {
|
||||
prepared_arg.push('\n');
|
||||
chars.next();
|
||||
}
|
||||
't' => {
|
||||
prepared_arg.push('\t');
|
||||
chars.next();
|
||||
}
|
||||
'r' => {
|
||||
prepared_arg.push('\r');
|
||||
chars.next();
|
||||
}
|
||||
'a' => {
|
||||
prepared_arg.push('\x07');
|
||||
chars.next();
|
||||
}
|
||||
'b' => {
|
||||
prepared_arg.push('\x08');
|
||||
chars.next();
|
||||
}
|
||||
'e' | 'E' => {
|
||||
prepared_arg.push('\x1b');
|
||||
chars.next();
|
||||
}
|
||||
'x' => {
|
||||
chars.next(); // consume 'x'
|
||||
let mut hex_digits = String::new();
|
||||
for _ in 0..2 {
|
||||
if let Some(&hex_char) = chars.peek() {
|
||||
if hex_char.is_ascii_hexdigit() {
|
||||
hex_digits.push(hex_char);
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Ok(value) = u8::from_str_radix(&hex_digits, 16) {
|
||||
prepared_arg.push(value as char);
|
||||
} else {
|
||||
prepared_arg.push('\\');
|
||||
prepared_arg.push('x');
|
||||
prepared_arg.push_str(&hex_digits);
|
||||
}
|
||||
}
|
||||
'0' => {
|
||||
chars.next(); // consume '0'
|
||||
let mut octal_digits = String::new();
|
||||
for _ in 0..3 {
|
||||
if let Some(&octal_char) = chars.peek() {
|
||||
if ('0'..='7').contains(&octal_char) {
|
||||
octal_digits.push(octal_char);
|
||||
chars.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Ok(value) = u8::from_str_radix(&octal_digits, 8) {
|
||||
prepared_arg.push(value as char);
|
||||
} else {
|
||||
prepared_arg.push('\\');
|
||||
prepared_arg.push('0');
|
||||
prepared_arg.push_str(&octal_digits);
|
||||
}
|
||||
}
|
||||
'\\' => {
|
||||
prepared_arg.push('\\');
|
||||
chars.next();
|
||||
}
|
||||
_ => prepared_arg.push(c),
|
||||
}
|
||||
} else {
|
||||
prepared_arg.push(c);
|
||||
}
|
||||
} else {
|
||||
prepared_arg.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
prepared_args.push(prepared_arg);
|
||||
}
|
||||
prepared_args.push(prepared_arg);
|
||||
}
|
||||
|
||||
Ok(prepared_args)
|
||||
Ok(prepared_args)
|
||||
}
|
||||
|
||||
pub fn get_echo_flags(opts: Vec<Opt>) -> ShResult<EchoFlags> {
|
||||
let mut flags = EchoFlags::empty();
|
||||
|
||||
for opt in opts {
|
||||
for opt in opts {
|
||||
match opt {
|
||||
Opt::Short('n') => flags |= EchoFlags::NO_NEWLINE,
|
||||
Opt::Short('r') => flags |= EchoFlags::USE_STDERR,
|
||||
Opt::Short('e') => flags |= EchoFlags::USE_ESCAPE,
|
||||
Opt::Short('p') => flags |= EchoFlags::USE_PROMPT,
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("echo: Unexpected flag '{opt}'"),
|
||||
));
|
||||
}
|
||||
Opt::Short('p') => flags |= EchoFlags::USE_PROMPT,
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("echo: Unexpected flag '{opt}'"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ pub fn export(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult
|
||||
for (arg, _) in argv {
|
||||
if let Some((var, val)) = arg.split_once('=') {
|
||||
write_vars(|v| v.set_var(var, val, VarFlags::EXPORT)); // Export an assignment like
|
||||
// 'foo=bar'
|
||||
// 'foo=bar'
|
||||
} else {
|
||||
write_vars(|v| v.export_var(&arg)); // Export an existing variable, if
|
||||
// any
|
||||
// any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
parse::{execute::prepare_argv, NdRule, Node},
|
||||
parse::{NdRule, Node, execute::prepare_argv},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
@@ -32,7 +32,6 @@ pub fn flowctl(node: Node, kind: ShErrKind) -> ShResult<()> {
|
||||
code = status;
|
||||
}
|
||||
|
||||
|
||||
let kind = match kind {
|
||||
LoopContinue(_) => LoopContinue(code),
|
||||
LoopBreak(_) => LoopBreak(code),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
jobs::{JobBldr, JobCmdFlags, JobID},
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
parse::{lex::Span, NdRule, Node},
|
||||
parse::{NdRule, Node, lex::Span},
|
||||
prelude::*,
|
||||
procio::{borrow_fd, IoStack},
|
||||
procio::{IoStack, borrow_fd},
|
||||
state::{self, read_jobs, write_jobs},
|
||||
};
|
||||
|
||||
@@ -168,7 +168,7 @@ pub fn jobs(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
ShErrKind::SyntaxErr,
|
||||
"Invalid flag in jobs call",
|
||||
span,
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
flags |= flag
|
||||
|
||||
@@ -4,7 +4,9 @@ use crate::{
|
||||
jobs::{ChildProc, JobBldr},
|
||||
libsh::error::ShResult,
|
||||
parse::{
|
||||
Redir, execute::prepare_argv, lex::{Span, Tk}
|
||||
Redir,
|
||||
execute::prepare_argv,
|
||||
lex::{Span, Tk},
|
||||
},
|
||||
procio::{IoFrame, IoStack, RedirGuard},
|
||||
};
|
||||
@@ -16,19 +18,17 @@ pub mod export;
|
||||
pub mod flowctl;
|
||||
pub mod jobctl;
|
||||
pub mod pwd;
|
||||
pub mod read;
|
||||
pub mod shift;
|
||||
pub mod shopt;
|
||||
pub mod source;
|
||||
pub mod test; // [[ ]] thing
|
||||
pub mod read;
|
||||
pub mod zoltraak;
|
||||
pub mod trap;
|
||||
pub mod zoltraak;
|
||||
|
||||
pub const BUILTINS: [&str; 21] = [
|
||||
"echo", "cd", "read", "export", "pwd", "source",
|
||||
"shift", "jobs", "fg", "bg", "alias", "unalias",
|
||||
"return", "break", "continue", "exit", "zoltraak",
|
||||
"shopt", "builtin", "command", "trap"
|
||||
"echo", "cd", "read", "export", "pwd", "source", "shift", "jobs", "fg", "bg", "alias", "unalias",
|
||||
"return", "break", "continue", "exit", "zoltraak", "shopt", "builtin", "command", "trap",
|
||||
];
|
||||
|
||||
/// Sets up a builtin command
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
libsh::error::ShResult,
|
||||
parse::{NdRule, Node},
|
||||
prelude::*,
|
||||
procio::{borrow_fd, IoStack},
|
||||
procio::{IoStack, borrow_fd},
|
||||
state,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,188 +1,237 @@
|
||||
use bitflags::bitflags;
|
||||
use nix::{errno::Errno, libc::{STDIN_FILENO, STDOUT_FILENO}, unistd::{isatty, read, write}};
|
||||
use nix::{
|
||||
errno::Errno,
|
||||
libc::{STDIN_FILENO, STDOUT_FILENO},
|
||||
unistd::{isatty, read, write},
|
||||
};
|
||||
|
||||
use crate::{builtin::setup_builtin, getopt::{Opt, OptSpec, get_opts_from_tokens}, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult, ShResultExt}, parse::{NdRule, Node}, procio::{IoStack, borrow_fd}, prompt::readline::term::RawModeGuard, state::{self, VarFlags, read_vars, write_vars}};
|
||||
use crate::{
|
||||
builtin::setup_builtin,
|
||||
getopt::{Opt, OptSpec, get_opts_from_tokens},
|
||||
jobs::JobBldr,
|
||||
libsh::error::{ShErr, ShErrKind, ShResult, ShResultExt},
|
||||
parse::{NdRule, Node},
|
||||
procio::{IoStack, borrow_fd},
|
||||
prompt::readline::term::RawModeGuard,
|
||||
state::{self, VarFlags, read_vars, write_vars},
|
||||
};
|
||||
|
||||
pub const READ_OPTS: [OptSpec;7] = [
|
||||
OptSpec { opt: Opt::Short('r'), takes_arg: false }, // don't allow backslash escapes
|
||||
OptSpec { opt: Opt::Short('s'), takes_arg: false }, // don't echo input
|
||||
OptSpec { opt: Opt::Short('a'), takes_arg: false }, // read into array
|
||||
OptSpec { opt: Opt::Short('n'), takes_arg: false }, // read only N characters
|
||||
OptSpec { opt: Opt::Short('t'), takes_arg: false }, // timeout
|
||||
OptSpec { opt: Opt::Short('p'), takes_arg: true }, // prompt
|
||||
OptSpec { opt: Opt::Short('d'), takes_arg: true }, // read until delimiter
|
||||
pub const READ_OPTS: [OptSpec; 7] = [
|
||||
OptSpec {
|
||||
opt: Opt::Short('r'),
|
||||
takes_arg: false,
|
||||
}, // don't allow backslash escapes
|
||||
OptSpec {
|
||||
opt: Opt::Short('s'),
|
||||
takes_arg: false,
|
||||
}, // don't echo input
|
||||
OptSpec {
|
||||
opt: Opt::Short('a'),
|
||||
takes_arg: false,
|
||||
}, // read into array
|
||||
OptSpec {
|
||||
opt: Opt::Short('n'),
|
||||
takes_arg: false,
|
||||
}, // read only N characters
|
||||
OptSpec {
|
||||
opt: Opt::Short('t'),
|
||||
takes_arg: false,
|
||||
}, // timeout
|
||||
OptSpec {
|
||||
opt: Opt::Short('p'),
|
||||
takes_arg: true,
|
||||
}, // prompt
|
||||
OptSpec {
|
||||
opt: Opt::Short('d'),
|
||||
takes_arg: true,
|
||||
}, // read until delimiter
|
||||
];
|
||||
|
||||
bitflags! {
|
||||
pub struct ReadFlags: u32 {
|
||||
const NO_ESCAPES = 0b000001;
|
||||
const NO_ECHO = 0b000010; // TODO: unused
|
||||
const ARRAY = 0b000100; // TODO: unused
|
||||
const N_CHARS = 0b001000; // TODO: unused
|
||||
const TIMEOUT = 0b010000; // TODO: unused
|
||||
}
|
||||
pub struct ReadFlags: u32 {
|
||||
const NO_ESCAPES = 0b000001;
|
||||
const NO_ECHO = 0b000010; // TODO: unused
|
||||
const ARRAY = 0b000100; // TODO: unused
|
||||
const N_CHARS = 0b001000; // TODO: unused
|
||||
const TIMEOUT = 0b010000; // TODO: unused
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadOpts {
|
||||
prompt: Option<String>,
|
||||
delim: u8, // byte representation of the delimiter character
|
||||
flags: ReadFlags,
|
||||
prompt: Option<String>,
|
||||
delim: u8, // byte representation of the delimiter character
|
||||
flags: ReadFlags,
|
||||
}
|
||||
|
||||
pub fn read_builtin(node: Node, _io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
|
||||
let blame = node.get_span().clone();
|
||||
let NdRule::Command {
|
||||
assignments: _,
|
||||
argv
|
||||
} = node.class else {
|
||||
unreachable!()
|
||||
};
|
||||
let blame = node.get_span().clone();
|
||||
let NdRule::Command {
|
||||
assignments: _,
|
||||
argv,
|
||||
} = node.class
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &READ_OPTS);
|
||||
let read_opts = get_read_flags(opts).blame(blame.clone())?;
|
||||
let (argv, _) = setup_builtin(argv, job, None).blame(blame.clone())?;
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &READ_OPTS);
|
||||
let read_opts = get_read_flags(opts).blame(blame.clone())?;
|
||||
let (argv, _) = setup_builtin(argv, job, None).blame(blame.clone())?;
|
||||
|
||||
if let Some(prompt) = read_opts.prompt {
|
||||
write(borrow_fd(STDOUT_FILENO), prompt.as_bytes())?;
|
||||
}
|
||||
if let Some(prompt) = read_opts.prompt {
|
||||
write(borrow_fd(STDOUT_FILENO), prompt.as_bytes())?;
|
||||
}
|
||||
|
||||
let input = if isatty(STDIN_FILENO)? {
|
||||
// Restore default terminal settings
|
||||
RawModeGuard::with_cooked_mode(|| {
|
||||
let mut input: Vec<u8> = vec![];
|
||||
let mut escaped = false;
|
||||
loop {
|
||||
let mut buf = [0u8;1];
|
||||
match read(STDIN_FILENO, &mut buf) {
|
||||
Ok(0) => {
|
||||
state::set_status(1);
|
||||
let str_result = String::from_utf8(input.clone()).map_err(|e| ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Input was not valid UTF-8: {e}"),
|
||||
))?;
|
||||
return Ok(str_result); // EOF
|
||||
}
|
||||
Ok(_) => {
|
||||
if buf[0] == read_opts.delim {
|
||||
if read_opts.flags.contains(ReadFlags::NO_ESCAPES) && escaped {
|
||||
input.push(buf[0]);
|
||||
} else {
|
||||
// Delimiter reached, stop reading
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if read_opts.flags.contains(ReadFlags::NO_ESCAPES)
|
||||
&& buf[0] == b'\\' {
|
||||
escaped = true;
|
||||
} else {
|
||||
input.push(buf[0]);
|
||||
}
|
||||
}
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(e) => return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Failed to read from stdin: {e}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
let input = if isatty(STDIN_FILENO)? {
|
||||
// Restore default terminal settings
|
||||
RawModeGuard::with_cooked_mode(|| {
|
||||
let mut input: Vec<u8> = vec![];
|
||||
let mut escaped = false;
|
||||
loop {
|
||||
let mut buf = [0u8; 1];
|
||||
match read(STDIN_FILENO, &mut buf) {
|
||||
Ok(0) => {
|
||||
state::set_status(1);
|
||||
let str_result = String::from_utf8(input.clone()).map_err(|e| {
|
||||
ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Input was not valid UTF-8: {e}"),
|
||||
)
|
||||
})?;
|
||||
return Ok(str_result); // EOF
|
||||
}
|
||||
Ok(_) => {
|
||||
if buf[0] == read_opts.delim {
|
||||
if read_opts.flags.contains(ReadFlags::NO_ESCAPES) && escaped {
|
||||
input.push(buf[0]);
|
||||
} else {
|
||||
// Delimiter reached, stop reading
|
||||
break;
|
||||
}
|
||||
} else if read_opts.flags.contains(ReadFlags::NO_ESCAPES) && buf[0] == b'\\' {
|
||||
escaped = true;
|
||||
} else {
|
||||
input.push(buf[0]);
|
||||
}
|
||||
}
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(e) => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Failed to read from stdin: {e}"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state::set_status(0);
|
||||
let str_result = String::from_utf8(input.clone()).map_err(|e| ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Input was not valid UTF-8: {e}"),
|
||||
))?;
|
||||
Ok(str_result)
|
||||
}).blame(blame)?
|
||||
} else {
|
||||
let mut input: Vec<u8> = vec![];
|
||||
loop {
|
||||
let mut buf = [0u8;1];
|
||||
match read(STDIN_FILENO, &mut buf) {
|
||||
Ok(0) => {
|
||||
state::set_status(1);
|
||||
break; // EOF
|
||||
}
|
||||
Ok(_) => {
|
||||
if buf[0] == read_opts.delim {
|
||||
break; // Delimiter reached, stop reading
|
||||
}
|
||||
input.push(buf[0]);
|
||||
}
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(e) => return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Failed to read from stdin: {e}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
String::from_utf8(input).map_err(|e| ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Input was not valid UTF-8: {e}"),
|
||||
))?
|
||||
};
|
||||
state::set_status(0);
|
||||
let str_result = String::from_utf8(input.clone()).map_err(|e| {
|
||||
ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Input was not valid UTF-8: {e}"),
|
||||
)
|
||||
})?;
|
||||
Ok(str_result)
|
||||
})
|
||||
.blame(blame)?
|
||||
} else {
|
||||
let mut input: Vec<u8> = vec![];
|
||||
loop {
|
||||
let mut buf = [0u8; 1];
|
||||
match read(STDIN_FILENO, &mut buf) {
|
||||
Ok(0) => {
|
||||
state::set_status(1);
|
||||
break; // EOF
|
||||
}
|
||||
Ok(_) => {
|
||||
if buf[0] == read_opts.delim {
|
||||
break; // Delimiter reached, stop reading
|
||||
}
|
||||
input.push(buf[0]);
|
||||
}
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(e) => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Failed to read from stdin: {e}"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
String::from_utf8(input).map_err(|e| {
|
||||
ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Input was not valid UTF-8: {e}"),
|
||||
)
|
||||
})?
|
||||
};
|
||||
|
||||
if argv.is_empty() {
|
||||
write_vars(|v| {
|
||||
v.set_var("REPLY", &input, VarFlags::NONE);
|
||||
});
|
||||
} else {
|
||||
// get our field separator
|
||||
let mut field_sep = read_vars(|v| v.get_var("IFS"));
|
||||
if field_sep.is_empty() { field_sep = " ".to_string() }
|
||||
let mut remaining = input;
|
||||
if argv.is_empty() {
|
||||
write_vars(|v| {
|
||||
v.set_var("REPLY", &input, VarFlags::NONE);
|
||||
});
|
||||
} else {
|
||||
// get our field separator
|
||||
let mut field_sep = read_vars(|v| v.get_var("IFS"));
|
||||
if field_sep.is_empty() {
|
||||
field_sep = " ".to_string()
|
||||
}
|
||||
let mut remaining = input;
|
||||
|
||||
for (i, arg) in argv.iter().enumerate() {
|
||||
if i == argv.len() - 1 {
|
||||
// Last arg, stuff the rest of the input into it
|
||||
write_vars(|v| v.set_var(&arg.0, &remaining, VarFlags::NONE));
|
||||
break;
|
||||
}
|
||||
for (i, arg) in argv.iter().enumerate() {
|
||||
if i == argv.len() - 1 {
|
||||
// Last arg, stuff the rest of the input into it
|
||||
write_vars(|v| v.set_var(&arg.0, &remaining, VarFlags::NONE));
|
||||
break;
|
||||
}
|
||||
|
||||
// trim leading IFS characters
|
||||
let trimmed = remaining.trim_start_matches(|c: char| field_sep.contains(c));
|
||||
// trim leading IFS characters
|
||||
let trimmed = remaining.trim_start_matches(|c: char| field_sep.contains(c));
|
||||
|
||||
if let Some(idx) = trimmed.find(|c: char| field_sep.contains(c)) {
|
||||
// We found a field separator, split at the char index
|
||||
let (field, rest) = trimmed.split_at(idx);
|
||||
write_vars(|v| v.set_var(&arg.0, field, VarFlags::NONE));
|
||||
if let Some(idx) = trimmed.find(|c: char| field_sep.contains(c)) {
|
||||
// We found a field separator, split at the char index
|
||||
let (field, rest) = trimmed.split_at(idx);
|
||||
write_vars(|v| v.set_var(&arg.0, field, VarFlags::NONE));
|
||||
|
||||
// note that this doesn't account for consecutive IFS characters, which is what that trim above is for
|
||||
remaining = rest.to_string();
|
||||
} else {
|
||||
write_vars(|v| v.set_var(&arg.0, trimmed, VarFlags::NONE));
|
||||
remaining.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// note that this doesn't account for consecutive IFS characters, which is what
|
||||
// that trim above is for
|
||||
remaining = rest.to_string();
|
||||
} else {
|
||||
write_vars(|v| v.set_var(&arg.0, trimmed, VarFlags::NONE));
|
||||
remaining.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_read_flags(opts: Vec<Opt>) -> ShResult<ReadOpts> {
|
||||
let mut read_opts = ReadOpts {
|
||||
prompt: None,
|
||||
delim: b'\n',
|
||||
flags: ReadFlags::empty(),
|
||||
};
|
||||
let mut read_opts = ReadOpts {
|
||||
prompt: None,
|
||||
delim: b'\n',
|
||||
flags: ReadFlags::empty(),
|
||||
};
|
||||
|
||||
for opt in opts {
|
||||
match opt {
|
||||
Opt::Short('r') => read_opts.flags |= ReadFlags::NO_ESCAPES,
|
||||
Opt::Short('s') => read_opts.flags |= ReadFlags::NO_ECHO,
|
||||
Opt::Short('a') => read_opts.flags |= ReadFlags::ARRAY,
|
||||
Opt::Short('n') => read_opts.flags |= ReadFlags::N_CHARS,
|
||||
Opt::Short('t') => read_opts.flags |= ReadFlags::TIMEOUT,
|
||||
Opt::ShortWithArg('p', prompt) => read_opts.prompt = Some(prompt),
|
||||
Opt::ShortWithArg('d', delim) => read_opts.delim = delim.chars().map(|c| c as u8).next().unwrap_or(b'\n'),
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Unexpected flag '{opt}'"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
for opt in opts {
|
||||
match opt {
|
||||
Opt::Short('r') => read_opts.flags |= ReadFlags::NO_ESCAPES,
|
||||
Opt::Short('s') => read_opts.flags |= ReadFlags::NO_ECHO,
|
||||
Opt::Short('a') => read_opts.flags |= ReadFlags::ARRAY,
|
||||
Opt::Short('n') => read_opts.flags |= ReadFlags::N_CHARS,
|
||||
Opt::Short('t') => read_opts.flags |= ReadFlags::TIMEOUT,
|
||||
Opt::ShortWithArg('p', prompt) => read_opts.prompt = Some(prompt),
|
||||
Opt::ShortWithArg('d', delim) => {
|
||||
read_opts.delim = delim.chars().map(|c| c as u8).next().unwrap_or(b'\n')
|
||||
}
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("read: Unexpected flag '{opt}'"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(read_opts)
|
||||
Ok(read_opts)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
libsh::error::{ShResult, ShResultExt},
|
||||
parse::{NdRule, Node},
|
||||
prelude::*,
|
||||
procio::{borrow_fd, IoStack},
|
||||
procio::{IoStack, borrow_fd},
|
||||
state::write_shopts,
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use regex::Regex;
|
||||
|
||||
use crate::{
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
parse::{ConjunctOp, NdRule, Node, TestCase, TEST_UNARY_OPS},
|
||||
parse::{ConjunctOp, NdRule, Node, TEST_UNARY_OPS, TestCase},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
@@ -254,7 +254,7 @@ pub fn double_bracket_test(node: Node) -> ShResult<bool> {
|
||||
msg: "Expected a binary operator in this test call; found a unary operator".into(),
|
||||
notes: vec![],
|
||||
span: err_span,
|
||||
})
|
||||
});
|
||||
}
|
||||
TestOp::StringEq => rhs.trim() == lhs.trim(),
|
||||
TestOp::StringNeq => rhs.trim() != lhs.trim(),
|
||||
|
||||
@@ -1,162 +1,171 @@
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use nix::{libc::{STDERR_FILENO, STDOUT_FILENO}, sys::signal::Signal, unistd::write};
|
||||
use nix::{
|
||||
libc::{STDERR_FILENO, STDOUT_FILENO},
|
||||
sys::signal::Signal,
|
||||
unistd::write,
|
||||
};
|
||||
|
||||
use crate::{builtin::setup_builtin, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{NdRule, Node}, procio::{IoStack, borrow_fd}, state::{self, read_logic, write_logic}};
|
||||
use crate::{
|
||||
builtin::setup_builtin,
|
||||
jobs::JobBldr,
|
||||
libsh::error::{ShErr, ShErrKind, ShResult},
|
||||
parse::{NdRule, Node},
|
||||
procio::{IoStack, borrow_fd},
|
||||
state::{self, read_logic, write_logic},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub enum TrapTarget {
|
||||
Exit,
|
||||
Error,
|
||||
Signal(Signal)
|
||||
Exit,
|
||||
Error,
|
||||
Signal(Signal),
|
||||
}
|
||||
|
||||
impl FromStr for TrapTarget {
|
||||
type Err = ShErr;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"EXIT" => Ok(TrapTarget::Exit),
|
||||
"ERR" => Ok(TrapTarget::Error),
|
||||
type Err = ShErr;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"EXIT" => Ok(TrapTarget::Exit),
|
||||
"ERR" => Ok(TrapTarget::Error),
|
||||
|
||||
"INT" => Ok(TrapTarget::Signal(Signal::SIGINT)),
|
||||
"QUIT" => Ok(TrapTarget::Signal(Signal::SIGQUIT)),
|
||||
"ILL" => Ok(TrapTarget::Signal(Signal::SIGILL)),
|
||||
"TRAP" => Ok(TrapTarget::Signal(Signal::SIGTRAP)),
|
||||
"ABRT" => Ok(TrapTarget::Signal(Signal::SIGABRT)),
|
||||
"BUS" => Ok(TrapTarget::Signal(Signal::SIGBUS)),
|
||||
"FPE" => Ok(TrapTarget::Signal(Signal::SIGFPE)),
|
||||
"KILL" => Ok(TrapTarget::Signal(Signal::SIGKILL)),
|
||||
"USR1" => Ok(TrapTarget::Signal(Signal::SIGUSR1)),
|
||||
"SEGV" => Ok(TrapTarget::Signal(Signal::SIGSEGV)),
|
||||
"USR2" => Ok(TrapTarget::Signal(Signal::SIGUSR2)),
|
||||
"PIPE" => Ok(TrapTarget::Signal(Signal::SIGPIPE)),
|
||||
"ALRM" => Ok(TrapTarget::Signal(Signal::SIGALRM)),
|
||||
"TERM" => Ok(TrapTarget::Signal(Signal::SIGTERM)),
|
||||
"STKFLT" => Ok(TrapTarget::Signal(Signal::SIGSTKFLT)),
|
||||
"CHLD" => Ok(TrapTarget::Signal(Signal::SIGCHLD)),
|
||||
"CONT" => Ok(TrapTarget::Signal(Signal::SIGCONT)),
|
||||
"STOP" => Ok(TrapTarget::Signal(Signal::SIGSTOP)),
|
||||
"TSTP" => Ok(TrapTarget::Signal(Signal::SIGTSTP)),
|
||||
"TTIN" => Ok(TrapTarget::Signal(Signal::SIGTTIN)),
|
||||
"TTOU" => Ok(TrapTarget::Signal(Signal::SIGTTOU)),
|
||||
"URG" => Ok(TrapTarget::Signal(Signal::SIGURG)),
|
||||
"XCPU" => Ok(TrapTarget::Signal(Signal::SIGXCPU)),
|
||||
"XFSZ" => Ok(TrapTarget::Signal(Signal::SIGXFSZ)),
|
||||
"VTALRM" => Ok(TrapTarget::Signal(Signal::SIGVTALRM)),
|
||||
"PROF" => Ok(TrapTarget::Signal(Signal::SIGPROF)),
|
||||
"WINCH" => Ok(TrapTarget::Signal(Signal::SIGWINCH)),
|
||||
"IO" => Ok(TrapTarget::Signal(Signal::SIGIO)),
|
||||
"PWR" => Ok(TrapTarget::Signal(Signal::SIGPWR)),
|
||||
"SYS" => Ok(TrapTarget::Signal(Signal::SIGSYS)),
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("invalid trap target '{}'", s),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
"INT" => Ok(TrapTarget::Signal(Signal::SIGINT)),
|
||||
"QUIT" => Ok(TrapTarget::Signal(Signal::SIGQUIT)),
|
||||
"ILL" => Ok(TrapTarget::Signal(Signal::SIGILL)),
|
||||
"TRAP" => Ok(TrapTarget::Signal(Signal::SIGTRAP)),
|
||||
"ABRT" => Ok(TrapTarget::Signal(Signal::SIGABRT)),
|
||||
"BUS" => Ok(TrapTarget::Signal(Signal::SIGBUS)),
|
||||
"FPE" => Ok(TrapTarget::Signal(Signal::SIGFPE)),
|
||||
"KILL" => Ok(TrapTarget::Signal(Signal::SIGKILL)),
|
||||
"USR1" => Ok(TrapTarget::Signal(Signal::SIGUSR1)),
|
||||
"SEGV" => Ok(TrapTarget::Signal(Signal::SIGSEGV)),
|
||||
"USR2" => Ok(TrapTarget::Signal(Signal::SIGUSR2)),
|
||||
"PIPE" => Ok(TrapTarget::Signal(Signal::SIGPIPE)),
|
||||
"ALRM" => Ok(TrapTarget::Signal(Signal::SIGALRM)),
|
||||
"TERM" => Ok(TrapTarget::Signal(Signal::SIGTERM)),
|
||||
"STKFLT" => Ok(TrapTarget::Signal(Signal::SIGSTKFLT)),
|
||||
"CHLD" => Ok(TrapTarget::Signal(Signal::SIGCHLD)),
|
||||
"CONT" => Ok(TrapTarget::Signal(Signal::SIGCONT)),
|
||||
"STOP" => Ok(TrapTarget::Signal(Signal::SIGSTOP)),
|
||||
"TSTP" => Ok(TrapTarget::Signal(Signal::SIGTSTP)),
|
||||
"TTIN" => Ok(TrapTarget::Signal(Signal::SIGTTIN)),
|
||||
"TTOU" => Ok(TrapTarget::Signal(Signal::SIGTTOU)),
|
||||
"URG" => Ok(TrapTarget::Signal(Signal::SIGURG)),
|
||||
"XCPU" => Ok(TrapTarget::Signal(Signal::SIGXCPU)),
|
||||
"XFSZ" => Ok(TrapTarget::Signal(Signal::SIGXFSZ)),
|
||||
"VTALRM" => Ok(TrapTarget::Signal(Signal::SIGVTALRM)),
|
||||
"PROF" => Ok(TrapTarget::Signal(Signal::SIGPROF)),
|
||||
"WINCH" => Ok(TrapTarget::Signal(Signal::SIGWINCH)),
|
||||
"IO" => Ok(TrapTarget::Signal(Signal::SIGIO)),
|
||||
"PWR" => Ok(TrapTarget::Signal(Signal::SIGPWR)),
|
||||
"SYS" => Ok(TrapTarget::Signal(Signal::SIGSYS)),
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("invalid trap target '{}'", s),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TrapTarget {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TrapTarget::Exit => write!(f, "EXIT"),
|
||||
TrapTarget::Error => write!(f, "ERR"),
|
||||
TrapTarget::Signal(s) => {
|
||||
match s {
|
||||
Signal::SIGHUP => write!(f, "HUP"),
|
||||
Signal::SIGINT => write!(f, "INT"),
|
||||
Signal::SIGQUIT => write!(f, "QUIT"),
|
||||
Signal::SIGILL => write!(f, "ILL"),
|
||||
Signal::SIGTRAP => write!(f, "TRAP"),
|
||||
Signal::SIGABRT => write!(f, "ABRT"),
|
||||
Signal::SIGBUS => write!(f, "BUS"),
|
||||
Signal::SIGFPE => write!(f, "FPE"),
|
||||
Signal::SIGKILL => write!(f, "KILL"),
|
||||
Signal::SIGUSR1 => write!(f, "USR1"),
|
||||
Signal::SIGSEGV => write!(f, "SEGV"),
|
||||
Signal::SIGUSR2 => write!(f, "USR2"),
|
||||
Signal::SIGPIPE => write!(f, "PIPE"),
|
||||
Signal::SIGALRM => write!(f, "ALRM"),
|
||||
Signal::SIGTERM => write!(f, "TERM"),
|
||||
Signal::SIGSTKFLT => write!(f, "STKFLT"),
|
||||
Signal::SIGCHLD => write!(f, "CHLD"),
|
||||
Signal::SIGCONT => write!(f, "CONT"),
|
||||
Signal::SIGSTOP => write!(f, "STOP"),
|
||||
Signal::SIGTSTP => write!(f, "TSTP"),
|
||||
Signal::SIGTTIN => write!(f, "TTIN"),
|
||||
Signal::SIGTTOU => write!(f, "TTOU"),
|
||||
Signal::SIGURG => write!(f, "URG"),
|
||||
Signal::SIGXCPU => write!(f, "XCPU"),
|
||||
Signal::SIGXFSZ => write!(f, "XFSZ"),
|
||||
Signal::SIGVTALRM => write!(f, "VTALRM"),
|
||||
Signal::SIGPROF => write!(f, "PROF"),
|
||||
Signal::SIGWINCH => write!(f, "WINCH"),
|
||||
Signal::SIGIO => write!(f, "IO"),
|
||||
Signal::SIGPWR => write!(f, "PWR"),
|
||||
Signal::SIGSYS => write!(f, "SYS"),
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TrapTarget::Exit => write!(f, "EXIT"),
|
||||
TrapTarget::Error => write!(f, "ERR"),
|
||||
TrapTarget::Signal(s) => match s {
|
||||
Signal::SIGHUP => write!(f, "HUP"),
|
||||
Signal::SIGINT => write!(f, "INT"),
|
||||
Signal::SIGQUIT => write!(f, "QUIT"),
|
||||
Signal::SIGILL => write!(f, "ILL"),
|
||||
Signal::SIGTRAP => write!(f, "TRAP"),
|
||||
Signal::SIGABRT => write!(f, "ABRT"),
|
||||
Signal::SIGBUS => write!(f, "BUS"),
|
||||
Signal::SIGFPE => write!(f, "FPE"),
|
||||
Signal::SIGKILL => write!(f, "KILL"),
|
||||
Signal::SIGUSR1 => write!(f, "USR1"),
|
||||
Signal::SIGSEGV => write!(f, "SEGV"),
|
||||
Signal::SIGUSR2 => write!(f, "USR2"),
|
||||
Signal::SIGPIPE => write!(f, "PIPE"),
|
||||
Signal::SIGALRM => write!(f, "ALRM"),
|
||||
Signal::SIGTERM => write!(f, "TERM"),
|
||||
Signal::SIGSTKFLT => write!(f, "STKFLT"),
|
||||
Signal::SIGCHLD => write!(f, "CHLD"),
|
||||
Signal::SIGCONT => write!(f, "CONT"),
|
||||
Signal::SIGSTOP => write!(f, "STOP"),
|
||||
Signal::SIGTSTP => write!(f, "TSTP"),
|
||||
Signal::SIGTTIN => write!(f, "TTIN"),
|
||||
Signal::SIGTTOU => write!(f, "TTOU"),
|
||||
Signal::SIGURG => write!(f, "URG"),
|
||||
Signal::SIGXCPU => write!(f, "XCPU"),
|
||||
Signal::SIGXFSZ => write!(f, "XFSZ"),
|
||||
Signal::SIGVTALRM => write!(f, "VTALRM"),
|
||||
Signal::SIGPROF => write!(f, "PROF"),
|
||||
Signal::SIGWINCH => write!(f, "WINCH"),
|
||||
Signal::SIGIO => write!(f, "IO"),
|
||||
Signal::SIGPWR => write!(f, "PWR"),
|
||||
Signal::SIGSYS => write!(f, "SYS"),
|
||||
|
||||
_ => {
|
||||
log::warn!("TrapTarget::fmt() : unrecognized signal {}", s);
|
||||
Err(std::fmt::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
log::warn!("TrapTarget::fmt() : unrecognized signal {}", s);
|
||||
Err(std::fmt::Error)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trap(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
|
||||
let span = node.get_span();
|
||||
let NdRule::Command {
|
||||
assignments: _,
|
||||
argv,
|
||||
} = node.class
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let span = node.get_span();
|
||||
let NdRule::Command {
|
||||
assignments: _,
|
||||
argv,
|
||||
} = node.class
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
if argv.is_empty() {
|
||||
let stdout = borrow_fd(STDOUT_FILENO);
|
||||
if argv.is_empty() {
|
||||
let stdout = borrow_fd(STDOUT_FILENO);
|
||||
|
||||
return read_logic(|l| -> ShResult<()> {
|
||||
for l in l.traps() {
|
||||
let target = l.0;
|
||||
let command = l.1;
|
||||
write(stdout, format!("trap -- '{command}' {target}\n").as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
return read_logic(|l| -> ShResult<()> {
|
||||
for l in l.traps() {
|
||||
let target = l.0;
|
||||
let command = l.1;
|
||||
write(stdout, format!("trap -- '{command}' {target}\n").as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
if argv.len() == 1 {
|
||||
let stderr = borrow_fd(STDERR_FILENO);
|
||||
write(stderr, b"usage: trap <COMMAND> [SIGNAL...]\n")?;
|
||||
state::set_status(1);
|
||||
return Ok(())
|
||||
}
|
||||
if argv.len() == 1 {
|
||||
let stderr = borrow_fd(STDERR_FILENO);
|
||||
write(stderr, b"usage: trap <COMMAND> [SIGNAL...]\n")?;
|
||||
state::set_status(1);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut args = argv.into_iter();
|
||||
let mut args = argv.into_iter();
|
||||
|
||||
let command = args.next().unwrap().0;
|
||||
let mut targets = vec![];
|
||||
let command = args.next().unwrap().0;
|
||||
let mut targets = vec![];
|
||||
|
||||
while let Some((arg, _)) = args.next() {
|
||||
let target = arg.parse::<TrapTarget>()?;
|
||||
targets.push(target);
|
||||
}
|
||||
while let Some((arg, _)) = args.next() {
|
||||
let target = arg.parse::<TrapTarget>()?;
|
||||
targets.push(target);
|
||||
}
|
||||
|
||||
for target in targets {
|
||||
if &command == "-" {
|
||||
write_logic(|l| l.remove_trap(target))
|
||||
} else {
|
||||
write_logic(|l| l.insert_trap(target, command.clone()))
|
||||
}
|
||||
}
|
||||
for target in targets {
|
||||
if &command == "-" {
|
||||
write_logic(|l| l.remove_trap(target))
|
||||
} else {
|
||||
write_logic(|l| l.insert_trap(target, command.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -37,14 +37,32 @@ pub fn zoltraak(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResu
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let zolt_opts = [
|
||||
OptSpec { opt: Opt::Long("dry-run".into()), takes_arg: false },
|
||||
OptSpec { opt: Opt::Long("confirm".into()), takes_arg: false },
|
||||
OptSpec { opt: Opt::Long("no-preserve-root".into()), takes_arg: false },
|
||||
OptSpec { opt: Opt::Short('r'), takes_arg: false },
|
||||
OptSpec { opt: Opt::Short('f'), takes_arg: false },
|
||||
OptSpec { opt: Opt::Short('v'), takes_arg: false }
|
||||
];
|
||||
let zolt_opts = [
|
||||
OptSpec {
|
||||
opt: Opt::Long("dry-run".into()),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Long("confirm".into()),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Long("no-preserve-root".into()),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Short('r'),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Short('f'),
|
||||
takes_arg: false,
|
||||
},
|
||||
OptSpec {
|
||||
opt: Opt::Short('v'),
|
||||
takes_arg: false,
|
||||
},
|
||||
];
|
||||
let mut flags = ZoltFlags::empty();
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &zolt_opts);
|
||||
@@ -56,41 +74,40 @@ pub fn zoltraak(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResu
|
||||
"confirm" => flags |= ZoltFlags::CONFIRM,
|
||||
"dry-run" => flags |= ZoltFlags::DRY,
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
},
|
||||
Opt::Short(flag) => match flag {
|
||||
'r' => flags |= ZoltFlags::RECURSIVE,
|
||||
'f' => flags |= ZoltFlags::FORCE,
|
||||
'v' => flags |= ZoltFlags::VERBOSE,
|
||||
_ => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
},
|
||||
Opt::LongWithArg(flag, _) => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
Opt::ShortWithArg(flag, _) => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
Opt::LongWithArg(flag, _) => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
Opt::ShortWithArg(flag, _) => {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{flag}'"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
|
||||
for (arg, span) in argv {
|
||||
if &arg == "/" && !flags.contains(ZoltFlags::NO_PRESERVE_ROOT) {
|
||||
return Err(
|
||||
@@ -109,7 +126,6 @@ pub fn zoltraak(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResu
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user