properly implemented read builtin
fixed bugs related to redirections and compound commands improved io routing logic
This commit is contained in:
@@ -18,7 +18,7 @@ pub fn alias(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, io_frame) = 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() {
|
||||
// Display the environment variables
|
||||
@@ -54,7 +54,6 @@ pub fn alias(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
write_logic(|l| l.insert_alias(name, body));
|
||||
}
|
||||
}
|
||||
io_frame.unwrap().restore()?;
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
}
|
||||
@@ -68,7 +67,7 @@ pub fn unalias(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResul
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, io_frame) = 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() {
|
||||
// Display the environment variables
|
||||
@@ -97,7 +96,6 @@ pub fn unalias(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResul
|
||||
write_logic(|l| l.remove_alias(&arg))
|
||||
}
|
||||
}
|
||||
io_frame.unwrap().restore()?;
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::{
|
||||
builtin::setup_builtin, expand::expand_prompt, getopt::{Opt, OptSet, 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 static ECHO_OPTS: LazyLock<OptSet> = LazyLock::new(|| {
|
||||
[
|
||||
Opt::Short('n'),
|
||||
Opt::Short('E'),
|
||||
Opt::Short('e'),
|
||||
Opt::Short('p'),
|
||||
]
|
||||
.into()
|
||||
});
|
||||
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! {
|
||||
pub struct EchoFlags: u32 {
|
||||
@@ -33,9 +30,9 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
unreachable!()
|
||||
};
|
||||
assert!(!argv.is_empty());
|
||||
let (argv, opts) = get_opts_from_tokens(argv);
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &ECHO_OPTS);
|
||||
let flags = get_echo_flags(opts).blame(blame)?;
|
||||
let (argv, io_frame) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
let output_channel = if flags.contains(EchoFlags::USE_STDERR) {
|
||||
borrow_fd(STDERR_FILENO)
|
||||
@@ -57,7 +54,6 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
|
||||
write(output_channel, echo_output.as_bytes())?;
|
||||
|
||||
io_frame.unwrap().restore()?;
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
}
|
||||
@@ -178,24 +174,21 @@ pub fn prepare_echo_args(argv: Vec<String>, use_escape: bool, use_prompt: bool)
|
||||
Ok(prepared_args)
|
||||
}
|
||||
|
||||
pub fn get_echo_flags(mut opts: Vec<Opt>) -> ShResult<EchoFlags> {
|
||||
pub fn get_echo_flags(opts: Vec<Opt>) -> ShResult<EchoFlags> {
|
||||
let mut flags = EchoFlags::empty();
|
||||
|
||||
while let Some(opt) = opts.pop() {
|
||||
if !ECHO_OPTS.contains(&opt) {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::ExecFail,
|
||||
format!("echo: Unexpected flag '{opt}'"),
|
||||
));
|
||||
}
|
||||
let Opt::Short(opt) = opt else { unreachable!() };
|
||||
|
||||
for opt in opts {
|
||||
match opt {
|
||||
'n' => flags |= EchoFlags::NO_NEWLINE,
|
||||
'r' => flags |= EchoFlags::USE_STDERR,
|
||||
'e' => flags |= EchoFlags::USE_ESCAPE,
|
||||
'p' => flags |= EchoFlags::USE_PROMPT,
|
||||
_ => unreachable!(),
|
||||
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}'"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ pub fn export(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, io_frame) = 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() {
|
||||
// Display the environment variables
|
||||
@@ -42,7 +42,6 @@ pub fn export(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult
|
||||
}
|
||||
}
|
||||
}
|
||||
io_frame.unwrap().restore()?;
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ pub fn jobs(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, io_frame) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
let mut flags = JobCmdFlags::empty();
|
||||
for (arg, span) in argv {
|
||||
@@ -175,7 +175,6 @@ pub fn jobs(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
|
||||
}
|
||||
}
|
||||
write_jobs(|j| j.print_jobs(flags))?;
|
||||
io_frame.unwrap().restore()?;
|
||||
state::set_status(0);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -4,11 +4,9 @@ use crate::{
|
||||
jobs::{ChildProc, JobBldr},
|
||||
libsh::error::ShResult,
|
||||
parse::{
|
||||
execute::prepare_argv,
|
||||
lex::{Span, Tk},
|
||||
Redir,
|
||||
Redir, execute::prepare_argv, lex::{Span, Tk}
|
||||
},
|
||||
procio::{IoFrame, IoStack},
|
||||
procio::{IoFrame, IoStack, RedirGuard},
|
||||
};
|
||||
|
||||
pub mod alias;
|
||||
@@ -21,12 +19,15 @@ pub mod pwd;
|
||||
pub mod shift;
|
||||
pub mod shopt;
|
||||
pub mod source;
|
||||
pub mod test;
|
||||
pub mod zoltraak; // [[ ]] thing
|
||||
pub mod test; // [[ ]] thing
|
||||
pub mod read;
|
||||
pub mod zoltraak;
|
||||
|
||||
pub const BUILTINS: [&str; 19] = [
|
||||
"echo", "cd", "export", "pwd", "source", "shift", "jobs", "fg", "bg", "alias", "unalias",
|
||||
"return", "break", "continue", "exit", "zoltraak", "shopt", "builtin", "command",
|
||||
pub const BUILTINS: [&str; 20] = [
|
||||
"echo", "cd", "read", "export", "pwd", "source",
|
||||
"shift", "jobs", "fg", "bg", "alias", "unalias",
|
||||
"return", "break", "continue", "exit", "zoltraak",
|
||||
"shopt", "builtin", "command",
|
||||
];
|
||||
|
||||
/// Sets up a builtin command
|
||||
@@ -56,7 +57,7 @@ pub const BUILTINS: [&str; 19] = [
|
||||
/// * If redirections are given, the second field of the resulting tuple will
|
||||
/// *always* be `Some()`
|
||||
/// * If no redirections are given, the second field will *always* be `None`
|
||||
type SetupReturns = ShResult<(Vec<(String, Span)>, Option<IoFrame>)>;
|
||||
type SetupReturns = ShResult<(Vec<(String, Span)>, Option<RedirGuard>)>;
|
||||
pub fn setup_builtin(
|
||||
argv: Vec<Tk>,
|
||||
job: &mut JobBldr,
|
||||
@@ -74,16 +75,16 @@ pub fn setup_builtin(
|
||||
let child = ChildProc::new(Pid::this(), Some(&cmd_name), Some(child_pgid))?;
|
||||
job.push_child(child);
|
||||
|
||||
let io_frame = if let Some((io_stack, redirs)) = io_mode {
|
||||
let guard = if let Some((io_stack, redirs)) = io_mode {
|
||||
io_stack.append_to_frame(redirs);
|
||||
let mut io_frame = io_stack.pop_frame();
|
||||
io_frame.redirect()?;
|
||||
Some(io_frame)
|
||||
let io_frame = io_stack.pop_frame();
|
||||
let guard = io_frame.redirect()?;
|
||||
Some(guard)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// We return the io_frame because the caller needs to also call
|
||||
// io_frame.restore()
|
||||
Ok((argv, io_frame))
|
||||
Ok((argv, guard))
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ pub fn pwd(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (_, io_frame) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (_, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
let stdout = borrow_fd(STDOUT_FILENO);
|
||||
|
||||
@@ -26,7 +26,6 @@ pub fn pwd(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()
|
||||
curr_dir.push('\n');
|
||||
write(stdout, curr_dir.as_bytes())?;
|
||||
|
||||
io_frame.unwrap().restore().unwrap();
|
||||
state::set_status(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
187
src/builtin/read.rs
Normal file
187
src/builtin/read.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
use bitflags::bitflags;
|
||||
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}};
|
||||
|
||||
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;
|
||||
const ARRAY = 0b000100;
|
||||
const N_CHARS = 0b001000;
|
||||
const TIMEOUT = 0b010000;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadOpts {
|
||||
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 (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())?;
|
||||
}
|
||||
|
||||
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}"),
|
||||
))?
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_read_flags(opts: Vec<Opt>) -> ShResult<ReadOpts> {
|
||||
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}'"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(read_opts)
|
||||
}
|
||||
@@ -18,10 +18,8 @@ pub fn shopt(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let (argv, io_frame) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
let mut io_frame = io_frame.unwrap();
|
||||
io_frame.redirect()?;
|
||||
for (arg, span) in argv {
|
||||
let Some(mut output) = write_shopts(|s| s.query(&arg)).blame(span)? else {
|
||||
continue;
|
||||
@@ -31,11 +29,9 @@ pub fn shopt(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<
|
||||
output.push('\n');
|
||||
|
||||
if let Err(e) = write(output_channel, output.as_bytes()) {
|
||||
io_frame.restore()?;
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
io_frame.restore()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
use std::{os::unix::fs::OpenOptionsExt, sync::LazyLock};
|
||||
|
||||
use crate::{
|
||||
getopt::{get_opts_from_tokens, Opt, OptSet},
|
||||
getopt::{Opt, OptSet, OptSpec, get_opts_from_tokens},
|
||||
jobs::JobBldr,
|
||||
libsh::error::{Note, ShErr, ShErrKind, ShResult, ShResultExt},
|
||||
parse::{NdRule, Node},
|
||||
prelude::*,
|
||||
procio::{borrow_fd, IoStack},
|
||||
procio::{IoStack, borrow_fd},
|
||||
};
|
||||
|
||||
use super::setup_builtin;
|
||||
|
||||
pub static ZOLTRAAK_OPTS: LazyLock<OptSet> = LazyLock::new(|| {
|
||||
[
|
||||
Opt::Long("dry-run".into()),
|
||||
Opt::Long("confirm".into()),
|
||||
Opt::Long("no-preserve-root".into()),
|
||||
Opt::Short('r'),
|
||||
Opt::Short('f'),
|
||||
Opt::Short('v'),
|
||||
]
|
||||
.into()
|
||||
});
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
|
||||
struct ZoltFlags: u32 {
|
||||
@@ -49,37 +37,59 @@ 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 mut flags = ZoltFlags::empty();
|
||||
|
||||
let (argv, opts) = get_opts_from_tokens(argv);
|
||||
let (argv, opts) = get_opts_from_tokens(argv, &zolt_opts);
|
||||
|
||||
for opt in opts {
|
||||
if !ZOLTRAAK_OPTS.contains(&opt) {
|
||||
return Err(ShErr::simple(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("zoltraak: unrecognized option '{opt}'"),
|
||||
));
|
||||
}
|
||||
match opt {
|
||||
Opt::Long(flag) => match flag.as_str() {
|
||||
"no-preserve-root" => flags |= ZoltFlags::NO_PRESERVE_ROOT,
|
||||
"confirm" => flags |= ZoltFlags::CONFIRM,
|
||||
"dry-run" => flags |= ZoltFlags::DRY,
|
||||
_ => unreachable!(),
|
||||
_ => {
|
||||
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,
|
||||
_ => unreachable!(),
|
||||
_ => {
|
||||
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, io_frame) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
|
||||
|
||||
let mut io_frame = io_frame.unwrap();
|
||||
io_frame.redirect()?;
|
||||
|
||||
for (arg, span) in argv {
|
||||
if &arg == "/" && !flags.contains(ZoltFlags::NO_PRESERVE_ROOT) {
|
||||
@@ -95,12 +105,10 @@ pub fn zoltraak(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResu
|
||||
);
|
||||
}
|
||||
if let Err(e) = annihilate(&arg, flags).blame(span) {
|
||||
io_frame.restore()?;
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
io_frame.restore()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user