Implemented scoping for expansions

This commit is contained in:
2025-03-08 01:38:42 -05:00
parent 67977f96eb
commit cdcfb23edb
18 changed files with 271 additions and 141 deletions

View File

@@ -2,52 +2,82 @@ use shellenv::jobs::{ChildProc, JobBldr};
use crate::prelude::*;
bitflags! {
#[derive(Debug,Clone,Copy)]
pub struct EchoFlags: u32 {
const USE_ESCAPE = 0b0001;
const NO_ESCAPE = 0b0010;
const STDERR = 0b0100;
const NO_NEWLINE = 0b1000;
}
}
pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
let rule = node.into_rule();
if let NdRule::Command { argv, redirs } = rule {
let argv = argv.drop_first().as_strings(shenv);
let mut formatted = argv.join(" ");
formatted.push('\n');
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
shenv.collect_redirs(redirs);
if let Err(e) = shenv.ctx_mut().activate_rdrs() {
eprintln!("{:?}",e);
exit(1);
}
if let Err(e) = write_out(formatted) {
eprintln!("{:?}",e);
exit(1);
}
exit(0);
} else {
match unsafe { fork()? } {
Child => {
shenv.collect_redirs(redirs);
if let Err(e) = shenv.ctx_mut().activate_rdrs() {
eprintln!("{:?}",e);
exit(1);
let mut argv_iter = argv.into_iter().skip(1).peekable();
let mut echo_flags = EchoFlags::empty();
while let Some(arg) = argv_iter.peek() {
let blame = arg.span();
let raw = arg.as_raw(shenv);
if raw.starts_with('-') {
let _ = argv_iter.next();
let mut options = raw.strip_prefix('-').unwrap().chars();
while let Some(opt) = options.next() {
match opt {
'r' => echo_flags |= EchoFlags::STDERR,
'n' => echo_flags |= EchoFlags::NO_NEWLINE,
'e' => {
if echo_flags.contains(EchoFlags::NO_ESCAPE) {
return Err(
ShErr::full(
ShErrKind::ExecFail,
"the 'e' and 'E' flags are mutually exclusive",
shenv.get_input(),
blame
)
)
}
echo_flags |= EchoFlags::USE_ESCAPE;
}
'E' => {
if echo_flags.contains(EchoFlags::USE_ESCAPE) {
return Err(
ShErr::full(
ShErrKind::ExecFail,
"the 'e' and 'E' flags are mutually exclusive",
shenv.get_input(),
blame
)
)
}
echo_flags |= EchoFlags::NO_ESCAPE;
}
_ => return Err(
ShErr::full(
ShErrKind::ExecFail,
format!("Unrecognized echo option"),
shenv.get_input(),
blame
)
)
}
if let Err(e) = write_out(formatted) {
eprintln!("{:?}",e);
exit(1);
}
exit(0);
}
Parent { child } => {
shenv.reset_io()?;
let children = vec![
ChildProc::new(child, Some("echo"), Some(child))?
];
let job = JobBldr::new()
.with_children(children)
.with_pgid(child)
.build();
wait_fg(job, shenv)?;
}
} else {
break
}
}
let argv = argv_iter.collect::<Vec<_>>().as_strings(shenv);
let mut formatted = argv.join(" ");
if !echo_flags.contains(EchoFlags::NO_NEWLINE) {
formatted.push('\n');
}
shenv.collect_redirs(redirs);
shenv.ctx_mut().activate_rdrs()?;
write_out(formatted)?;
} else { unreachable!() }
Ok(())
}