From 12f36283ca128dfc2ff8e5a57908bd8823df196c Mon Sep 17 00:00:00 2001 From: pagedmov Date: Mon, 16 Feb 2026 19:09:10 -0500 Subject: [PATCH] fixed empty arguments being filtered out during word splitting --- src/expand.rs | 8 +++----- src/main.rs | 4 ++-- src/parse/execute.rs | 14 ++++++++++---- src/state.rs | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/expand.rs b/src/expand.rs index 54c7a17..afc69b5 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -94,9 +94,7 @@ impl Expander { _ => cur_word.push(ch), } } - if !cur_word.is_empty() { - words.push(cur_word); - } + words.push(cur_word); words } } @@ -754,7 +752,7 @@ pub fn expand_proc_sub(raw: &str, is_input: bool) -> ShResult { let mut io_stack = IoStack::new(); io_stack.push_frame(io_frame); - if let Err(e) = exec_input(raw.to_string(), Some(io_stack)) { + if let Err(e) = exec_input(raw.to_string(), Some(io_stack), false) { eprintln!("{e}"); exit(1); } @@ -787,7 +785,7 @@ pub fn expand_cmd_sub(raw: &str) -> ShResult { match unsafe { fork()? } { ForkResult::Child => { io_stack.push_frame(cmd_sub_io_frame); - if let Err(e) = exec_input(raw.to_string(), Some(io_stack)) { + if let Err(e) = exec_input(raw.to_string(), Some(io_stack), false) { eprintln!("{e}"); unsafe { libc::_exit(1) }; } diff --git a/src/main.rs b/src/main.rs index 264e7b8..2809556 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,7 +101,7 @@ fn run_script>(path: P, args: Vec) -> ShResult<()> { write_vars(|v| v.cur_scope_mut().bpush_arg(arg)) } - exec_input(input, None) + exec_input(input, None, false) } fn fern_interactive() -> ShResult<()> { @@ -187,7 +187,7 @@ fn fern_interactive() -> ShResult<()> { match readline.process_input() { Ok(ReadlineEvent::Line(input)) => { write_meta(|m| m.start_timer()); - if let Err(e) = exec_input(input, None) { + if let Err(e) = exec_input(input, None, true) { match e.kind() { ShErrKind::CleanExit(code) => { QUIT_CODE.store(*code, Ordering::SeqCst); diff --git a/src/parse/execute.rs b/src/parse/execute.rs index 449f4eb..cac051f 100644 --- a/src/parse/execute.rs +++ b/src/parse/execute.rs @@ -116,7 +116,7 @@ impl ExecArgs { } } -pub fn exec_input(input: String, io_stack: Option) -> ShResult<()> { +pub fn exec_input(input: String, io_stack: Option, interactive: bool) -> ShResult<()> { let log_tab = read_logic(|l| l.clone()); let input = expand_aliases(input, HashSet::new(), &log_tab); let mut parser = ParsedSrc::new(Arc::new(input)); @@ -127,7 +127,7 @@ pub fn exec_input(input: String, io_stack: Option) -> ShResult<()> { return Ok(()); } - let mut dispatcher = Dispatcher::new(parser.extract_nodes()); + let mut dispatcher = Dispatcher::new(parser.extract_nodes(), interactive); if let Some(mut stack) = io_stack { dispatcher.io_stack.extend(stack.drain(..)); } @@ -136,15 +136,17 @@ pub fn exec_input(input: String, io_stack: Option) -> ShResult<()> { pub struct Dispatcher { nodes: VecDeque, + interactive: bool, pub io_stack: IoStack, pub job_stack: JobStack, } impl Dispatcher { - pub fn new(nodes: Vec) -> Self { + pub fn new(nodes: Vec, interactive: bool) -> Self { let nodes = VecDeque::from(nodes); Self { nodes, + interactive, io_stack: IoStack::new(), job_stack: JobStack::new(), } @@ -265,7 +267,7 @@ impl Dispatcher { let subsh_body = subsh.0.to_string(); let _guard = ScopeGuard::shared_scope(); - exec_input(subsh_body, None)?; + exec_input(subsh_body, None, self.interactive)?; Ok(()) } @@ -607,6 +609,10 @@ impl Dispatcher { self.io_stack.append_to_frame(cmd.redirs); let exec_args = ExecArgs::new(argv)?; + if self.interactive { + log::info!("expanded argv: {:?}", exec_args.argv.iter().map(|s| s.to_str().unwrap()).collect::>()); + } + let io_frame = self.io_stack.pop_frame(); run_fork( io_frame, diff --git a/src/state.rs b/src/state.rs index bc22e7d..a125dc1 100644 --- a/src/state.rs +++ b/src/state.rs @@ -802,6 +802,6 @@ pub fn source_file(path: PathBuf) -> ShResult<()> { let mut buf = String::new(); file.read_to_string(&mut buf)?; - exec_input(buf, None)?; + exec_input(buf, None, false)?; Ok(()) }