diff --git a/src/expand.rs b/src/expand.rs index 3fe06a1..66d524f 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -5,8 +5,8 @@ use std::str::{Chars, FromStr}; use glob::Pattern; use regex::Regex; -use crate::state::{read_vars, write_meta, write_vars, LogTab}; -use crate::procio::{IoBuf, IoFrame, IoMode}; +use crate::state::{read_jobs, read_vars, write_jobs, write_meta, write_vars, LogTab}; +use crate::procio::{IoBuf, IoFrame, IoMode, IoStack}; use crate::prelude::*; use crate::parse::{Redir, RedirType}; use crate::parse::execute::exec_input; @@ -23,6 +23,10 @@ pub const SNG_QUOTE: char = '\u{fdd2}'; pub const TILDE_SUB: char = '\u{fdd3}'; /// Subshell marker pub const SUBSH: char = '\u{fdd4}'; +/// Input process sub marker +pub const PROC_SUB_IN: char = '\u{fdd5}'; +/// Output process sub marker +pub const PROC_SUB_OUT: char = '\u{fdd6}'; impl Tk { /// Create a new expanded token @@ -105,6 +109,28 @@ pub fn expand_raw(chars: &mut Peekable>) -> ShResult { let home = env::var("HOME").unwrap_or_default(); result.push_str(&home); } + PROC_SUB_OUT =>{ + let mut inner = String::new(); + while let Some(ch) = chars.next() { + match ch { + PROC_SUB_OUT => break, + _ => inner.push(ch) + } + } + let fd_path = expand_proc_sub(&inner, false)?; + result.push_str(&fd_path); + } + PROC_SUB_IN =>{ + let mut inner = String::new(); + while let Some(ch) = chars.next() { + match ch { + PROC_SUB_IN => break, + _ => inner.push(ch) + } + } + let fd_path = expand_proc_sub(&inner, true)?; + result.push_str(&fd_path); + } VAR_SUB => { flog!(INFO, chars); let expanded = expand_var(chars)?; @@ -358,6 +384,41 @@ pub fn expand_arithmetic(raw: &str) -> ShResult { Ok(result.to_string()) } +pub fn expand_proc_sub(raw: &str, is_input: bool) -> ShResult { + // FIXME: Still a lot of issues here + // Seems like debugging will be a massive effort + let (rpipe, wpipe) = IoMode::get_pipes(); + let rpipe_raw = rpipe.src_fd(); + let wpipe_raw = wpipe.src_fd(); + + let (proc_fd, register_fd, redir_type, path) = match is_input { + false => (wpipe, rpipe, RedirType::Output, format!("/proc/self/fd/{}", rpipe_raw)), + true => (rpipe, wpipe, RedirType::Input, format!("/proc/self/fd/{}", wpipe_raw)), + }; + + match unsafe { fork()? } { + ForkResult::Child => { + let redir = Redir::new(proc_fd, redir_type); + let io_frame = IoFrame::from_redir(redir); + let mut io_stack = IoStack::new(); + io_stack.push_frame(io_frame); + + if let Err(e) = exec_input(raw.to_string(), Some(io_stack)) { + eprintln!("{e}"); + exit(1); + } + exit(0); + } + ForkResult::Parent { child } => { + write_jobs(|j| j.register_fd(child, register_fd)); + let registered = read_jobs(|j| j.registered_fds().to_vec()); + flog!(DEBUG,registered); + // Do not wait; process may run in background + Ok(path) + } + } +} + /// Get the command output of a given command input as a String pub fn expand_cmd_sub(raw: &str) -> ShResult { flog!(DEBUG, "in expand_cmd_sub"); @@ -369,17 +430,14 @@ pub fn expand_cmd_sub(raw: &str) -> ShResult { } let (rpipe,wpipe) = IoMode::get_pipes(); let cmd_sub_redir = Redir::new(wpipe, RedirType::Output); - let mut cmd_sub_io_frame = IoFrame::from_redir(cmd_sub_redir); + let cmd_sub_io_frame = IoFrame::from_redir(cmd_sub_redir); + let mut io_stack = IoStack::new(); let mut io_buf = IoBuf::new(rpipe); match unsafe { fork()? } { ForkResult::Child => { - if let Err(e) = cmd_sub_io_frame.redirect() { - eprintln!("{e}"); - exit(1); - } - - if let Err(e) = exec_input(raw.to_string()) { + io_stack.push_frame(cmd_sub_io_frame); + if let Err(e) = exec_input(raw.to_string(), Some(io_stack)) { eprintln!("{e}"); exit(1); } @@ -411,7 +469,6 @@ pub fn unescape_str(raw: &str) -> String { while let Some(ch) = chars.next() { - flog!(DEBUG,result); match ch { '~' if first_char => { result.push(TILDE_SUB) @@ -464,10 +521,9 @@ pub fn unescape_str(raw: &str) -> String { result.push(VAR_SUB); if chars.peek() == Some(&'(') { chars.next(); - let mut cmdsub_count = 1; + let mut paren_count = 1; result.push(SUBSH); while let Some(subsh_ch) = chars.next() { - flog!(DEBUG, subsh_ch); match subsh_ch { '\\' => { result.push(subsh_ch); @@ -475,13 +531,13 @@ pub fn unescape_str(raw: &str) -> String { result.push(next_ch) } } - '$' if chars.peek() == Some(&'(') => { + '(' => { result.push(subsh_ch); - cmdsub_count += 1; + paren_count += 1; } ')' => { - cmdsub_count -= 1; - if cmdsub_count <= 0 { + paren_count -= 1; + if paren_count <= 0 { result.push(SUBSH); break } else { @@ -513,12 +569,69 @@ pub fn unescape_str(raw: &str) -> String { } } } + '<' if chars.peek() == Some(&'(') => { + chars.next(); + let mut paren_count = 1; + result.push(PROC_SUB_OUT); + while let Some(subsh_ch) = chars.next() { + match subsh_ch { + '\\' => { + result.push(subsh_ch); + if let Some(next_ch) = chars.next() { + result.push(next_ch) + } + } + '(' => { + result.push(subsh_ch); + paren_count += 1; + } + ')' => { + paren_count -= 1; + if paren_count <= 0 { + result.push(PROC_SUB_OUT); + break + } else { + result.push(subsh_ch); + } + } + _ => result.push(subsh_ch), + } + } + } + '>' if chars.peek() == Some(&'(') => { + chars.next(); + let mut paren_count = 1; + result.push(PROC_SUB_IN); + while let Some(subsh_ch) = chars.next() { + match subsh_ch { + '\\' => { + result.push(subsh_ch); + if let Some(next_ch) = chars.next() { + result.push(next_ch) + } + } + '(' => { + result.push(subsh_ch); + paren_count += 1; + } + ')' => { + paren_count -= 1; + if paren_count <= 0 { + result.push(PROC_SUB_IN); + break + } else { + result.push(subsh_ch); + } + } + _ => result.push(subsh_ch), + } + } + } '$' => result.push(VAR_SUB), _ => result.push(ch) } first_char = false; } - flog!(DEBUG, result); result } @@ -1277,16 +1390,16 @@ pub fn expand_prompt(raw: &str) -> ShResult { } } PromptTk::Pwd => { - let mut pwd = std::env::var("PWD")?; - let home = std::env::var("HOME")?; + let mut pwd = std::env::var("PWD").unwrap(); + let home = std::env::var("HOME").unwrap(); if pwd.starts_with(&home) { pwd = pwd.replacen(&home, "~", 1); } result.push_str(&pwd); } PromptTk::PwdShort => { - let mut path = std::env::var("PWD")?; - let home = std::env::var("HOME")?; + let mut path = std::env::var("PWD").unwrap(); + let home = std::env::var("HOME").unwrap(); if path.starts_with(&home) { path = path.replacen(&home, "~", 1); } @@ -1305,17 +1418,17 @@ pub fn expand_prompt(raw: &str) -> ShResult { result.push_str(&path_rebuilt); } PromptTk::Hostname => { - let hostname = std::env::var("HOSTNAME")?; + let hostname = std::env::var("HOST").unwrap(); result.push_str(&hostname); } PromptTk::HostnameShort => todo!(), PromptTk::ShellName => result.push_str("fern"), PromptTk::Username => { - let username = std::env::var("USER")?; + let username = std::env::var("USER").unwrap(); result.push_str(&username); } PromptTk::PromptSymbol => { - let uid = std::env::var("UID")?; + let uid = std::env::var("UID").unwrap(); let symbol = if &uid == "0" { '#' } else { diff --git a/src/fern.rs b/src/fern.rs index 9529693..4ebb998 100644 --- a/src/fern.rs +++ b/src/fern.rs @@ -24,7 +24,7 @@ use crate::signal::sig_setup; use crate::state::source_rc; use crate::prelude::*; use clap::Parser; -use state::write_vars; +use state::{read_vars, write_vars}; #[derive(Parser,Debug)] struct FernArgs { @@ -37,8 +37,20 @@ struct FernArgs { version: bool } +/// Force evaluation of lazily-initialized values early in shell startup. +/// +/// In particular, this ensures that the variable table is initialized, which populates +/// environment variables from the system. If this initialization is deferred too long, +/// features like prompt expansion may fail due to missing environment variables. +/// +/// This function triggers initialization by calling `read_vars` with a no-op closure, +/// which forces access to the variable table and causes its `LazyLock` constructor to run. +fn kickstart_lazy_evals() { + read_vars(|_| {}); +} fn main() { + kickstart_lazy_evals(); let args = FernArgs::parse(); if args.version { println!("fern {}", env!("CARGO_PKG_VERSION")); @@ -68,7 +80,7 @@ fn run_script>(path: P, args: Vec) { write_vars(|v| v.bpush_arg(arg)) } - if let Err(e) = exec_input(input) { + if let Err(e) = exec_input(input,None) { eprintln!("{e}"); exit(1); } @@ -94,7 +106,7 @@ fn fern_interactive() { Err(e) => { eprintln!("{e}"); readline_err_count += 1; - if readline_err_count == 5 { + if readline_err_count == 20 { eprintln!("reached maximum readline error count, exiting"); break } else { @@ -103,7 +115,7 @@ fn fern_interactive() { } }; - if let Err(e) = exec_input(input) { + if let Err(e) = exec_input(input,None) { eprintln!("{e}"); } } diff --git a/src/jobs.rs b/src/jobs.rs index 0810c22..0739de8 100644 --- a/src/jobs.rs +++ b/src/jobs.rs @@ -1,4 +1,4 @@ -use crate::{libsh::{error::ShResult, term::{Style, Styled}}, prelude::*, procio::borrow_fd, state::{self, set_status, write_jobs}}; +use crate::{libsh::{error::ShResult, term::{Style, Styled}}, prelude::*, procio::{borrow_fd, IoMode}, state::{self, set_status, write_jobs}}; pub const SIG_EXIT_OFFSET: i32 = 128; @@ -120,12 +120,19 @@ impl ChildProc { } } +#[derive(Clone,Debug)] +pub struct RegisteredFd { + pub fd: IoMode, + pub owner_pid: Pid, +} + #[derive(Default,Debug)] pub struct JobTab { fg: Option, order: Vec, new_updates: Vec, - jobs: Vec> + jobs: Vec>, + fd_registry: Vec } impl JobTab { @@ -154,6 +161,19 @@ impl JobTab { pub fn prev_job(&self) -> Option { self.order.last().copied() } + pub fn close_job_fds(&mut self, pid: Pid) { + self.fd_registry.retain(|fd| fd.owner_pid != pid) + } + pub fn registered_fds(&self) -> &[RegisteredFd] { + &self.fd_registry + } + pub fn register_fd(&mut self, owner_pid: Pid, fd: IoMode) { + let registered_fd = RegisteredFd { + fd, + owner_pid + }; + self.fd_registry.push(registered_fd) + } fn prune_jobs(&mut self) { while let Some(job) = self.jobs.last() { if job.is_none() { diff --git a/src/parse/execute.rs b/src/parse/execute.rs index 6152e11..96bcb30 100644 --- a/src/parse/execute.rs +++ b/src/parse/execute.rs @@ -39,7 +39,7 @@ impl ExecArgs { } } -pub fn exec_input(input: String) -> ShResult<()> { +pub fn exec_input(input: String, io_stack: Option) -> ShResult<()> { write_meta(|m| m.start_timer()); let log_tab = LOGIC_TABLE.read().unwrap(); let input = expand_aliases(input, HashSet::new(), &log_tab); @@ -53,6 +53,9 @@ pub fn exec_input(input: String) -> ShResult<()> { } let mut dispatcher = Dispatcher::new(parser.extract_nodes()); + if let Some(mut stack) = io_stack { + dispatcher.io_stack.extend(stack.drain(..)); + } dispatcher.begin_dispatch() } @@ -176,7 +179,7 @@ impl Dispatcher { let subsh_body = subsh.0.to_string(); let snapshot = get_snapshots(); - if let Err(e) = exec_input(subsh_body) { + if let Err(e) = exec_input(subsh_body, None) { restore_snapshot(snapshot); return Err(e) } diff --git a/src/parse/lex.rs b/src/parse/lex.rs index 33666cb..9098d2d 100644 --- a/src/parse/lex.rs +++ b/src/parse/lex.rs @@ -2,7 +2,7 @@ use std::{collections::VecDeque, fmt::Display, iter::Peekable, ops::{Bound, Dere use bitflags::bitflags; -use crate::{builtin::BUILTINS, libsh::{error::{ShErr, ShErrKind, ShResult}, utils::CharDequeUtils}, parse::parse_err_full, prelude::*}; +use crate::{builtin::BUILTINS, libsh::{error::{ShErr, ShErrKind, ShResult}, utils::CharDequeUtils}, prelude::*}; pub const KEYWORDS: [&str;16] = [ "if", @@ -136,15 +136,16 @@ impl Display for Tk { bitflags! { #[derive(Debug,Clone,Copy,PartialEq,Default)] pub struct TkFlags: u32 { - const KEYWORD = 0b0000000000000001; - /// This is a keyword that opens a new block statement, like 'if' and 'while' - const OPENER = 0b0000000000000010; - const IS_CMD = 0b0000000000000100; - const IS_SUBSH = 0b0000000000001000; - const IS_CMDSUB = 0b0000000000010000; - const IS_OP = 0b0000000000100000; - const ASSIGN = 0b0000000001000000; - const BUILTIN = 0b0000000010000000; + const KEYWORD = 0b0000000000000001; + /// This is a keyword that opens a new block statement, like 'if' and 'while' + const OPENER = 0b0000000000000010; + const IS_CMD = 0b0000000000000100; + const IS_SUBSH = 0b0000000000001000; + const IS_CMDSUB = 0b0000000000010000; + const IS_OP = 0b0000000000100000; + const ASSIGN = 0b0000000001000000; + const BUILTIN = 0b0000000010000000; + const IS_PROCSUB = 0b0000000100000000; } } @@ -242,6 +243,9 @@ impl LexStream { while let Some(ch) = chars.next() { match ch { '>' => { + if chars.peek() == Some(&'(') { + return None // It's a process sub + } pos += 1; if let Some('>') = chars.peek() { chars.next(); @@ -277,6 +281,9 @@ impl LexStream { } } '<' => { + if chars.peek() == Some(&'(') { + return None // It's a process sub + } pos += 1; for _ in 0..2 { @@ -327,7 +334,6 @@ impl LexStream { } while let Some(ch) = chars.next() { - flog!(DEBUG, "we are in the loop"); match ch { _ if self.flags.contains(LexFlags::RAW) => { if ch.is_whitespace() { @@ -342,61 +348,6 @@ impl LexStream { pos += ch.len_utf8(); } } - '`' => { - let arith_pos = pos; - pos += 1; - let mut closed = false; - while let Some(arith_ch) = chars.next() { - match arith_ch { - '\\' => { - pos += 1; - if let Some(ch) = chars.next() { - pos += ch.len_utf8(); - } - } - '`' => { - pos += 1; - closed = true; - break - } - '$' if chars.peek() == Some(&'(') => { - pos += 2; - chars.next(); - let mut cmdsub_count = 1; - while let Some(cmdsub_ch) = chars.next() { - match cmdsub_ch { - '$' if chars.peek() == Some(&'(') => { - pos += 2; - chars.next(); - cmdsub_count += 1; - } - ')' => { - pos += 1; - cmdsub_count -= 1; - if cmdsub_count == 0 { - break - } - } - _ => pos += cmdsub_ch.len_utf8() - } - } - } - _ => pos += arith_ch.len_utf8() - } - } - flog!(DEBUG, "we have left the loop"); - flog!(DEBUG, closed); - if !closed && !self.flags.contains(LexFlags::LEX_UNFINISHED) { - self.cursor = pos; - return Err( - ShErr::full( - ShErrKind::ParseErr, - "Unclosed arithmetic substitution", - Span::new(arith_pos..arith_pos + 1, self.source.clone()) - ) - ) - } - } '$' if chars.peek() == Some(&'{') => { pos += 2; chars.next(); @@ -424,6 +375,90 @@ impl LexStream { } } } + '<' if chars.peek() == Some(&'(') => { + pos += 2; + chars.next(); + let mut paren_count = 1; + let paren_pos = pos; + while let Some(ch) = chars.next() { + match ch { + '\\' => { + pos += 1; + if let Some(next_ch) = chars.next() { + pos += next_ch.len_utf8(); + } + } + '(' => { + pos += 1; + paren_count += 1; + } + ')' => { + pos += 1; + paren_count -= 1; + if paren_count <= 0 { + break + } + } + _ => pos += ch.len_utf8() + } + } + if !paren_count == 0 && !self.flags.contains(LexFlags::LEX_UNFINISHED) { + self.cursor = pos; + return Err( + ShErr::full( + ShErrKind::ParseErr, + "Unclosed subshell", + Span::new(paren_pos..paren_pos + 1, self.source.clone()) + ) + ) + } + let mut proc_sub_tk = self.get_token(self.cursor..pos, TkRule::Str); + proc_sub_tk.flags |= TkFlags::IS_PROCSUB; + self.cursor = pos; + return Ok(proc_sub_tk) + } + '>' if chars.peek() == Some(&'(') => { + pos += 2; + chars.next(); + let mut paren_count = 1; + let paren_pos = pos; + while let Some(ch) = chars.next() { + match ch { + '\\' => { + pos += 1; + if let Some(next_ch) = chars.next() { + pos += next_ch.len_utf8(); + } + } + '(' => { + pos += 1; + paren_count += 1; + } + ')' => { + pos += 1; + paren_count -= 1; + if paren_count <= 0 { + break + } + } + _ => pos += ch.len_utf8() + } + } + if !paren_count == 0 && !self.flags.contains(LexFlags::LEX_UNFINISHED) { + self.cursor = pos; + return Err( + ShErr::full( + ShErrKind::ParseErr, + "Unclosed subshell", + Span::new(paren_pos..paren_pos + 1, self.source.clone()) + ) + ) + } + let mut proc_sub_tk = self.get_token(self.cursor..pos, TkRule::Str); + proc_sub_tk.flags |= TkFlags::IS_PROCSUB; + self.cursor = pos; + return Ok(proc_sub_tk) + } '$' if chars.peek() == Some(&'(') => { pos += 2; chars.next(); @@ -507,7 +542,6 @@ impl LexStream { subsh_tk.flags |= TkFlags::IS_SUBSH; self.cursor = pos; self.set_next_is_cmd(true); - flog!(DEBUG, "returning subsh tk"); return Ok(subsh_tk) } '{' if pos == self.cursor && self.next_is_cmd() => { @@ -672,8 +706,6 @@ impl LexStream { impl Iterator for LexStream { type Item = ShResult; fn next(&mut self) -> Option { - flog!(DEBUG,self.cursor); - flog!(DEBUG,self.source.len()); assert!(self.cursor <= self.source.len()); // We are at the end of the input if self.cursor == self.source.len() { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 0fe9f50..8eb27d8 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -529,7 +529,6 @@ impl ParseStream { /// Ordered from specialized to general, with more generally matchable stuff appearing at the bottom /// The check_pipelines parameter is used to prevent left-recursion issues in self.parse_pipeln() fn parse_block(&mut self, check_pipelines: bool) -> ShResult> { - flog!(DEBUG, self.tokens); try_match!(self.parse_func_def()?); try_match!(self.parse_brc_grp(false /* from_func_def */)?); try_match!(self.parse_case()?); @@ -618,15 +617,12 @@ impl ParseStream { fn parse_test(&mut self) -> ShResult> { let mut node_tks: Vec = vec![]; let mut cases: Vec = vec![]; - flog!(INFO, self.check_keyword("[[")); if !self.check_keyword("[[") || !self.next_tk_is_some() { return Ok(None) } node_tks.push(self.next_tk().unwrap()); let mut case_builder = TestCaseBuilder::new(); while let Some(tk) = self.next_tk() { - flog!(DEBUG, case_builder); - flog!(DEBUG, tk.as_str()); node_tks.push(tk.clone()); if tk.as_str() == "]]" { if case_builder.can_build() { @@ -642,24 +638,19 @@ impl ParseStream { } } if case_builder.is_empty() { - flog!(DEBUG, "case builder is empty"); match tk.as_str() { _ if TEST_UNARY_OPS.contains(&tk.as_str()) => case_builder = case_builder.with_operator(tk.clone()), _ => case_builder = case_builder.with_lhs(tk.clone()) } continue } else if case_builder.operator.is_some() && case_builder.rhs.is_none() { - flog!(DEBUG, "op is some, rhs is none"); case_builder = case_builder.with_rhs(tk.clone()); continue } else if case_builder.lhs.is_some() && case_builder.operator.is_none() { - flog!(DEBUG, "lhs is some, op is none"); // we got lhs, then rhs → treat it as operator maybe? case_builder = case_builder.with_operator(tk.clone()); continue } else if let TkRule::And | TkRule::Or = tk.class { - flog!(DEBUG, "found conjunction"); - flog!(DEBUG, tk.class); if case_builder.can_build() { if case_builder.conjunct.is_some() { return Err( @@ -674,7 +665,6 @@ impl ParseStream { case_builder = case_builder.with_conjunction(op); let case = case_builder.build_and_take(); cases.push(case); - flog!(DEBUG, case_builder); continue } else { return Err( @@ -694,7 +684,6 @@ impl ParseStream { redirs: vec![], tokens: node_tks }; - flog!(DEBUG, node); Ok(Some(node)) } fn parse_brc_grp(&mut self, from_func_def: bool) -> ShResult> { @@ -1114,7 +1103,6 @@ impl ParseStream { node_tks.push(loop_tk); self.catch_separator(&mut node_tks); - flog!(DEBUG, node_tks); let Some(cond) = self.parse_block(true)? else { self.panic_mode(&mut node_tks); return Err(parse_err_full( @@ -1225,7 +1213,6 @@ impl ParseStream { return Ok(None) } } - flog!(DEBUG, argv); if argv.is_empty() && assignments.is_empty() { return Ok(None) diff --git a/src/prompt/mod.rs b/src/prompt/mod.rs index ed6f367..6d52879 100644 --- a/src/prompt/mod.rs +++ b/src/prompt/mod.rs @@ -6,7 +6,7 @@ use std::path::Path; use readline::FernReadline; use rustyline::{error::ReadlineError, history::FileHistory, ColorMode, Config, Editor}; -use crate::{expand::expand_prompt, libsh::{error::ShResult, term::{Style, Styled}}, prelude::*, state::read_shopts}; +use crate::{expand::expand_prompt, libsh::error::ShResult, prelude::*, state::read_shopts}; /// Initialize the line editor fn init_rl() -> ShResult> { @@ -45,7 +45,11 @@ fn init_rl() -> ShResult> { fn get_prompt() -> ShResult { let Ok(prompt) = env::var("PS1") else { - return Ok("$ ".styled(Style::Green | Style::Bold)) + // username@hostname + // short/path/to/pwd/ + // $ + let default = "\\e[1;0m\\u\\e[1;36m@\\e[1;31m\\h\\n\\e[1;36m\\W\\e[1;32m/\\n\\e[1;32m\\$ "; + return Ok(format!("\n{}",expand_prompt(default)?)) }; Ok(format!("\n{}",expand_prompt(&prompt)?)) diff --git a/src/signal.rs b/src/signal.rs index 86e22eb..af73f58 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -120,6 +120,7 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> { * We can reasonably assume that if it is not a foreground job, then it exists in the job table * If this assumption is incorrect, the code has gone wrong somewhere. */ + write_jobs(|j| j.close_job_fds(pid)); if let Some(( pgid, is_fg, @@ -132,6 +133,7 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> { job.update_by_id(JobID::Pid(pid), status).unwrap(); let is_finished = !job.running(); + if let Some(child) = job.children_mut().iter_mut().find(|chld| pid == chld.pid()) { child.set_stat(status); } diff --git a/src/state.rs b/src/state.rs index 5c0882d..36b8d4c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -170,7 +170,7 @@ impl VarTab { let hostname = gethostname().map(|hname| hname.to_string_lossy().to_string()).unwrap_or_default(); env::set_var("IFS", " \t\n"); - env::set_var("HOSTNAME", hostname); + env::set_var("HOST", hostname.clone()); env::set_var("UID", uid.to_string()); env::set_var("PPID", getppid().to_string()); env::set_var("TMPDIR", "/tmp"); @@ -448,6 +448,6 @@ pub fn source_file(path: PathBuf) -> ShResult<()> { let mut buf = String::new(); file.read_to_string(&mut buf)?; - exec_input(buf)?; + exec_input(buf,None)?; Ok(()) }