1use crate::command_data::Run;
2use crate::parse::parse_line;
3use crate::platform::{OsSignal, Pid, Platform, STDIN_FILENO, Sys, TermSettings};
4use std::collections::HashMap;
5use std::ffi::{OsStr, OsString};
6use std::fmt::{Display, Formatter};
7use std::{env, fmt, io};
8
9#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum JobStatus {
12 New,
14 Running,
16 Stopped,
18 Done,
20}
21
22impl fmt::Display for JobStatus {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 match self {
25 JobStatus::New => write!(f, "New"),
26 JobStatus::Running => write!(f, "Running"),
27 JobStatus::Stopped => write!(f, "Stopped"),
28 JobStatus::Done => write!(f, "Done"),
29 }
30 }
31}
32
33#[derive(Copy, Clone, Debug)]
35pub enum PidStatus {
36 Running(Pid),
38 Done(Pid, i32), Error(Pid),
42 Signaled(Pid, OsSignal),
44}
45
46impl PidStatus {
47 pub fn pid(&self) -> Pid {
48 match self {
49 PidStatus::Running(pid) => *pid,
50 PidStatus::Done(pid, _) => *pid,
51 PidStatus::Error(pid) => *pid,
52 PidStatus::Signaled(pid, _) => *pid,
53 }
54 }
55}
56
57#[derive(Clone, Debug)]
58pub struct Job {
59 id: u32,
60 shell_pid: Pid,
61 pgid: Pid,
62 pids: Vec<PidStatus>,
63 names: Vec<String>,
64 status: JobStatus,
65 interactive: bool,
66 stealth: bool, }
68
69impl Job {
70 fn new(id: u32, shell_pid: Pid, interactive: bool) -> Self {
72 Self {
73 id,
74 shell_pid,
75 pgid: shell_pid,
76 pids: vec![],
77 names: vec![],
78 status: JobStatus::New,
79 interactive,
80 stealth: false,
81 }
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.pids.is_empty()
87 }
88
89 pub fn pgid(&self) -> Pid {
91 self.pgid
92 }
93
94 pub fn pids(&self) -> &[PidStatus] {
96 &self.pids[..]
97 }
98
99 pub fn status(&self) -> JobStatus {
101 self.status
102 }
103
104 pub fn mark_stopped(&mut self) {
106 self.status = JobStatus::Stopped;
107 }
108
109 pub fn mark_running(&mut self) {
111 self.status = JobStatus::Running;
112 }
113
114 pub fn mark_done(&mut self) {
116 self.status = JobStatus::Done;
117 }
118
119 pub fn add_process(&mut self, pid: Pid, name: impl Into<String>) {
122 if self.pids.is_empty() {
123 self.pgid = pid;
124 }
125 self.pids.push(PidStatus::Running(pid));
126 self.names.push(name.into());
127 }
128
129 pub fn process_done(&mut self, pid: Pid, status: i32) {
132 for proc in self.pids.iter_mut() {
133 if proc.pid() == pid {
134 *proc = PidStatus::Done(pid, status);
135 return;
136 }
137 }
138 panic!("process {pid} not part of job");
139 }
140
141 pub fn process_error(&mut self, pid: Pid) {
144 for proc in self.pids.iter_mut() {
145 if proc.pid() == pid {
146 *proc = PidStatus::Error(pid);
147 return;
148 }
149 }
150 panic!("process {pid} not part of job");
151 }
152
153 pub fn process_signaled(&mut self, pid: Pid, signal: OsSignal) {
156 for proc in self.pids.iter_mut() {
157 if proc.pid() == pid {
158 *proc = PidStatus::Signaled(pid, signal);
159 return;
160 }
161 }
162 panic!("process {pid} not part of job");
163 }
164
165 pub fn reverse(&mut self) {
167 self.pids.reverse();
168 self.names.reverse();
169 }
170
171 pub fn set_stealth(&mut self, stealth: bool) {
173 self.stealth = stealth;
174 }
175
176 pub fn stealth(&self) -> bool {
178 self.stealth
179 }
180
181 pub fn interactive(&self) -> bool {
183 self.interactive
184 }
185
186 pub fn shell_pid(&self) -> Pid {
188 self.shell_pid
189 }
190}
191
192impl Display for Job {
193 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
194 writeln!(
195 f,
196 "[{}]\t{}\t{:?}\t{:?}",
197 self.id, self.status, self.pids, self.names
198 )
199 }
200}
201
202pub struct Jobs {
203 next_job: u32,
204 jobs: Vec<Job>,
205 shell_pid: Pid,
206 interactive: bool,
207 term_settings: Option<TermSettings>,
208 alias: HashMap<String, Run>,
209 local_vars: HashMap<OsString, OsString>,
210}
211
212impl Jobs {
213 pub fn new(interactive: bool) -> Self {
214 let shell_pid = Sys::getpid();
215 let term_settings = if interactive {
216 Sys::get_term_settings(STDIN_FILENO).ok()
217 } else {
218 None
219 };
220 Self {
221 next_job: 0,
222 jobs: vec![],
223 shell_pid,
224 interactive,
225 term_settings,
226 alias: HashMap::new(),
227 local_vars: HashMap::new(),
228 }
229 }
230
231 pub fn cap_term(&mut self) {
232 self.term_settings = Sys::get_term_settings(STDIN_FILENO).ok();
233 }
234
235 pub fn new_job(&mut self) -> Job {
238 if self.jobs.is_empty() {
239 self.next_job = 0;
240 }
241 self.next_job += 1;
243 Job::new(self.next_job, self.shell_pid, self.interactive)
244 }
245
246 pub fn push_job(&mut self, job: Job) {
248 self.jobs.push(job);
249 }
250
251 pub fn set_no_tty(&mut self) {
253 self.term_settings = None;
254 }
255
256 pub fn set_interactive(&mut self, interactive: bool) {
258 self.interactive = interactive;
259 }
260
261 pub fn get_job_mut(&mut self, job_id: u32) -> Option<&mut Job> {
263 self.jobs.iter_mut().find(|job| job.id == job_id)
264 }
265
266 pub fn reap_procs(&mut self) {
268 for job in self.jobs.iter_mut() {
269 if let JobStatus::Running = job.status() {
270 let pids: Vec<Pid> = job.pids().iter().map(|s| s.pid()).collect();
271 for pid in &pids {
272 Sys::try_wait_pid(*pid, job);
273 }
274 let mut done = true;
275 for pid_status in job.pids() {
276 if let PidStatus::Running(_) = pid_status {
277 done = false;
278 }
279 }
280 if done {
281 job.mark_done();
282 }
283 }
284 }
285 let jobs_len = self.jobs.len();
287 for i in (0..jobs_len).rev() {
288 if let JobStatus::Done = self.jobs[i].status() {
289 if !self.jobs[i].stealth() {
290 eprintln!("{}", self.jobs[i]);
291 }
292 self.jobs.remove(i);
293 }
294 }
295 }
296
297 pub fn foreground_job(&mut self, job_num: u32) {
299 let term_settings = self.term_settings.clone();
300 if let Some(job) = self.get_job_mut(job_num) {
301 if let Err(err) = Sys::foreground_job(job, &term_settings) {
302 eprintln!("Error making job {job_num} foreground in parent: {err}");
303 }
304 } else {
305 eprintln!("job number {job_num} is invalid");
306 }
307 }
308
309 pub fn background_job(&mut self, job_num: u32) {
311 if let Some(job) = self.get_job_mut(job_num) {
312 if let Err(err) = Sys::background_job(job) {
313 eprintln!("Error making job {job_num} background in parent: {err}");
314 }
315 } else {
316 eprintln!("job number {job_num} is invalid");
317 }
318 }
319
320 pub fn restore_terminal(&self) {
322 if let Some(term_settings) = &self.term_settings {
325 if let Err(err) = Sys::restore_terminal(term_settings, self.shell_pid) {
327 eprintln!("Error resetting shell terminal settings: {}", err);
328 }
329 }
330 }
331
332 pub fn get_alias<S: AsRef<str>>(&self, name: S) -> Option<Run> {
334 self.alias.get(name.as_ref()).cloned()
335 }
336
337 pub fn clear_aliases(&mut self) {
339 self.alias.clear();
340 }
341
342 pub fn remove_alias<S: AsRef<str>>(&mut self, name: S) -> Option<Run> {
344 self.alias.remove(name.as_ref())
345 }
346
347 pub fn add_alias(&mut self, name: String, value: String) -> Result<(), io::Error> {
349 let runj = parse_line(self, &value)?;
350 self.alias.insert(name, runj.into_run());
351 Ok(())
352 }
353
354 pub fn add_alias_run(&mut self, name: String, value: Run) {
356 self.alias.insert(name, value);
357 }
358
359 pub fn print_alias(&self, name: String) {
361 if let Some(value) = self.alias.get(&name) {
362 println!("alias {name}='{value}'");
363 } else {
364 eprintln!("no alias set for: {name}");
365 }
366 }
367
368 pub fn print_all_alias(&self) {
370 for (name, value) in &self.alias {
371 println!("alias {name}='{value}'");
372 }
373 }
374
375 pub fn set_local_var(&mut self, key: OsString, val: OsString) {
378 self.local_vars.insert(key, val);
379 }
380
381 pub fn get_local_var(&self, key: &OsStr) -> Option<&OsStr> {
383 self.local_vars.get(key).map(|s| s as &OsStr)
384 }
385
386 pub fn remove_local_var(&mut self, key: &OsStr) -> Option<OsString> {
389 self.local_vars.remove(key)
390 }
391
392 pub fn get_env_or_local_var(&self, key: &OsStr) -> Option<OsString> {
395 if let Some(val) = env::var_os(key) {
396 Some(val)
397 } else {
398 self.get_local_var(key).map(|val| val.to_os_string())
399 }
400 }
401}
402
403impl Display for Jobs {
404 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
405 for job in &self.jobs {
406 writeln!(f, "{job}")?;
407 }
408 Ok(())
409 }
410}