shell/
command_data.rs

1use crate::jobs::Jobs;
2use crate::platform::{FileDesc, FromFileDesc, Platform, STDIN_FILENO, STDOUT_FILENO, Sys};
3use std::collections::HashSet;
4use std::ffi::OsString;
5use std::fmt::{Display, Formatter};
6use std::fs::File;
7use std::io;
8use std::io::{BufRead, Write};
9use std::str::FromStr;
10
11/// Arg to a command, either a direct string or a sub command to run to get the arg.
12#[derive(Clone, Debug)]
13pub enum Arg {
14    /// Single string arg.
15    Str(OsString),
16    /// A command to run to get the string arg.
17    Command(Run),
18    /// Env variable to use to set the arg.
19    Var(OsString),
20    /// List of args that will be concatenated to make the arg.
21    Compound(Vec<Arg>),
22}
23
24impl Arg {
25    pub fn resolve_arg(&self, jobs: &mut Jobs) -> io::Result<OsString> {
26        match self {
27            Self::Str(val) => Ok(val.clone()),
28            Self::Command(run) => {
29                let (input, output) = Sys::anon_pipe()?;
30                let mut run = run.clone();
31                run.push_stdout_front(Some(output));
32                let mut job = jobs.new_job();
33                Sys::fork_run(&run, &mut job, jobs)?;
34                let lines = io::BufReader::new(unsafe { File::from_file_desc(input) }).lines();
35                let mut val = String::new();
36                for (i, line) in lines.enumerate() {
37                    if i > 0 {
38                        val.push(' ');
39                    }
40                    let line = line?;
41                    val.push_str(line.trim());
42                }
43                Ok(val.into())
44            }
45            Self::Var(var_name) => {
46                if let Some(val) = jobs.get_env_or_local_var(var_name) {
47                    Ok(val)
48                } else {
49                    Ok("".into())
50                }
51            }
52            Self::Compound(cargs) => {
53                let mut val = String::new();
54                for a in cargs {
55                    val.push_str(a.resolve_arg(jobs)?.to_string_lossy().as_ref());
56                }
57                Ok(val.into())
58            }
59        }
60    }
61}
62
63impl TryFrom<&mut Arg> for FileDesc {
64    type Error = ();
65
66    fn try_from(arg: &mut Arg) -> Result<Self, Self::Error> {
67        if let Arg::Str(targ) = arg {
68            let fd = targ.to_string_lossy();
69            if fd.ends_with('-') {
70                match FileDesc::from_str(&fd[0..fd.len() - 1]) {
71                    Ok(fd) => Ok(fd),
72                    Err(_) => Err(()),
73                }
74            } else {
75                match FileDesc::from_str(&fd) {
76                    Ok(fd) => Ok(fd),
77                    Err(_) => Err(()),
78                }
79            }
80        } else {
81            Err(())
82        }
83    }
84}
85
86impl Display for Arg {
87    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88        match self {
89            Self::Str(os_str) => write!(f, "{}", os_str.to_string_lossy()),
90            Self::Command(run) => write!(f, "$({run})"),
91            Self::Var(var) => write!(f, "${}", var.to_string_lossy()),
92            Self::Compound(cargs) => {
93                for a in cargs {
94                    write!(f, "{a}")?;
95                }
96                Ok(())
97            }
98        }
99    }
100}
101
102/// An argument for a redirect (the source).
103#[derive(Clone, Debug)]
104enum RedirArg {
105    /// Arg should resolve to a file path.
106    Path(Arg),
107    /// Arg should resolve to file descriptor (positive integer or '-' to close).
108    Fd(Arg),
109    /// A file descriptor created and managed by the shell (for instance for pipes).
110    InternalFd(FileDesc),
111}
112
113impl Display for RedirArg {
114    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115        match self {
116            RedirArg::Path(arg) => write!(f, "{arg}"),
117            RedirArg::Fd(arg) => write!(f, "&{arg}"),
118            RedirArg::InternalFd(_fd) => write!(f, ""),
119        }
120    }
121}
122
123/// An individual redirect, first element is always the target file descriptor.
124#[derive(Clone, Debug)]
125enum RedirType {
126    /// An input file to open and dup to fd.
127    In(FileDesc, RedirArg),
128    /// Inject Arg as data into the fd.
129    InDirect(FileDesc, Arg),
130    /// An output file to open (append) and dup to fd.
131    Out(FileDesc, RedirArg),
132    /// An output file to open (create/trunc) and dup to fd.
133    OutTrunc(FileDesc, RedirArg),
134    /// An input/output file to open (append) and dup to fd.
135    InOut(FileDesc, RedirArg),
136}
137
138impl Display for RedirType {
139    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
140        match self {
141            RedirType::In(fd, arg) => write!(f, " {fd}<{arg}"),
142            RedirType::InDirect(fd, arg) => write!(f, " {fd}<<{arg}"),
143            RedirType::Out(fd, arg) => write!(f, " {fd}>>{arg}"),
144            RedirType::OutTrunc(fd, arg) => write!(f, " {fd}>{arg}"),
145            RedirType::InOut(fd, arg) => write!(f, " {fd}<>{arg}"),
146        }
147    }
148}
149
150impl RedirType {
151    fn process_source_fd(
152        dest_fd: FileDesc,
153        arg: &Arg,
154        jobs: &mut Jobs,
155    ) -> Result<FileDesc, io::Error> {
156        let targ = arg.resolve_arg(jobs)?;
157        let source_fd = targ.to_string_lossy();
158        if source_fd == "-" {
159            Sys::close_fd(dest_fd)?;
160            Ok(dest_fd)
161        } else if source_fd.ends_with('-') {
162            match FileDesc::from_str(&source_fd[0..source_fd.len() - 1]) {
163                Ok(source_fd) => {
164                    Sys::dup2_fd(source_fd, dest_fd)?;
165                    Sys::close_fd(source_fd)?;
166                    Ok(dest_fd)
167                }
168                Err(err) => Err(io::Error::other(err)),
169            }
170        } else {
171            match FileDesc::from_str(&source_fd) {
172                Ok(source_fd) => Sys::dup2_fd(source_fd, dest_fd),
173                Err(err) => Err(io::Error::other(err)),
174            }
175        }
176    }
177
178    fn process(&self, jobs: &mut Jobs) -> Result<FileDesc, io::Error> {
179        match self {
180            RedirType::In(fd, RedirArg::Path(arg)) => {
181                let path = arg.resolve_arg(jobs)?;
182                let f = File::open(path)?;
183                // Use as_raw_fd ot nto_raw_fd so f will close when dropped.
184                Sys::dup2_fd(f.into(), *fd)?;
185                Ok(*fd)
186            }
187            RedirType::In(fd, RedirArg::Fd(arg)) => Self::process_source_fd(*fd, arg, jobs),
188            RedirType::InDirect(fd, arg) => {
189                let tdata = arg.resolve_arg(jobs)?;
190                let data = tdata.to_string_lossy();
191                if let Ok((pread, pwrite)) = Sys::anon_pipe() {
192                    Sys::dup2_fd(pread, *fd)?;
193                    Sys::close_fd(pread)?;
194                    unsafe {
195                        let mut file = File::from_file_desc(pwrite);
196                        if let Err(e) = file.write_all(data.as_bytes()) {
197                            eprintln!("Error writing {data} to fd {fd}: {e}");
198                        }
199                    }
200                }
201                Ok(*fd)
202            }
203            RedirType::In(dest_fd, RedirArg::InternalFd(source_fd)) => {
204                Sys::dup2_fd(*source_fd, *dest_fd)
205            }
206            RedirType::Out(fd, RedirArg::Path(arg)) => {
207                let path = arg.resolve_arg(jobs)?;
208                let f = File::options().append(true).create(true).open(path)?;
209                // Use as_raw_fd ot nto_raw_fd so f will close when dropped.
210                Sys::dup2_fd(f.into(), *fd)?;
211                Ok(*fd)
212            }
213            RedirType::Out(fd, RedirArg::Fd(arg)) => Self::process_source_fd(*fd, arg, jobs),
214            RedirType::Out(dest_fd, RedirArg::InternalFd(source_fd)) => {
215                Sys::dup2_fd(*source_fd, *dest_fd)
216            }
217            RedirType::OutTrunc(fd, RedirArg::Path(arg)) => {
218                let path = arg.resolve_arg(jobs)?;
219                let f = File::create(path)?;
220                // Use as_raw_fd ot nto_raw_fd so f will close when dropped.
221                Sys::dup2_fd(f.into(), *fd)?;
222                Ok(*fd)
223            }
224            RedirType::OutTrunc(fd, RedirArg::Fd(arg)) => Self::process_source_fd(*fd, arg, jobs),
225            RedirType::OutTrunc(dest_fd, RedirArg::InternalFd(source_fd)) => {
226                Sys::dup2_fd(*source_fd, *dest_fd)
227            }
228            RedirType::InOut(fd, RedirArg::Path(arg)) => {
229                let path = arg.resolve_arg(jobs)?;
230                let f = File::options()
231                    .append(true)
232                    .create(true)
233                    .read(true)
234                    .open(path)?;
235                // Use as_raw_fd ot nto_raw_fd so f will close when dropped.
236                Sys::dup2_fd(f.into(), *fd)?;
237                Ok(*fd)
238            }
239            RedirType::InOut(fd, RedirArg::Fd(arg)) => Self::process_source_fd(*fd, arg, jobs),
240            RedirType::InOut(dest_fd, RedirArg::InternalFd(source_fd)) => {
241                Sys::dup2_fd(*source_fd, *dest_fd)
242            }
243        }
244    }
245}
246
247/// Contains a stack or redirects that can be processed in order to setup the file descriptors for
248/// a new process.
249#[derive(Clone, Debug)]
250pub struct Redirects {
251    redir_stack: Vec<RedirType>,
252}
253
254impl Display for Redirects {
255    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
256        for r in &self.redir_stack {
257            write!(f, "{r}")?;
258        }
259        Ok(())
260    }
261}
262
263impl Redirects {
264    /// Create a new empty redirect stack.
265    pub fn new() -> Self {
266        Self {
267            redir_stack: Vec::new(),
268        }
269    }
270
271    /// Process the stack in order and setup all the requested file descriptors.
272    /// Returns a Set containing all the file descriptors setup (to avoid closing them on process start).
273    pub fn process(&self, jobs: &mut Jobs) -> Result<HashSet<FileDesc>, io::Error> {
274        let mut fd_set = HashSet::new();
275        for r in &self.redir_stack {
276            fd_set.insert(r.process(jobs)?);
277        }
278        Ok(fd_set)
279    }
280
281    /// Push a fd to fd redirect onto the stack.
282    /// This is for internally managed source FDs (for pipes etc).
283    /// If push_back is true then push to the end else put on the front (useful for pipes).
284    pub fn set_in_internal_fd(&mut self, dest_fd: FileDesc, source_fd: FileDesc, push_back: bool) {
285        let redir = RedirType::In(dest_fd, RedirArg::InternalFd(source_fd));
286        if push_back {
287            self.redir_stack.push(redir);
288        } else {
289            self.redir_stack.insert(0, redir);
290        }
291    }
292
293    /// Push a fd to fd redirect onto the stack.
294    /// This is for internally managed source FDs (for pipes etc).
295    /// If push_back is true then push to the end else put on the front (useful for pipes).
296    pub fn set_out_internal_fd(&mut self, dest_fd: FileDesc, source_fd: FileDesc, push_back: bool) {
297        let redir = RedirType::Out(dest_fd, RedirArg::InternalFd(source_fd));
298        if push_back {
299            self.redir_stack.push(redir);
300        } else {
301            self.redir_stack.insert(0, redir);
302        }
303    }
304
305    /// Push a fd to fd redirect onto the stack.
306    /// If push_back is true then push to the end else put on the front (useful for pipes).
307    pub fn set_in_fd(&mut self, dest_fd: FileDesc, source_fd: Arg, push_back: bool) {
308        let redir = RedirType::In(dest_fd, RedirArg::Fd(source_fd));
309        if push_back {
310            self.redir_stack.push(redir);
311        } else {
312            self.redir_stack.insert(0, redir);
313        }
314    }
315
316    /// Push a fd to fd redirect onto the stack.
317    /// If push_back is true then push to the end else put on the front (useful for pipes).
318    pub fn set_out_fd(&mut self, dest_fd: FileDesc, source_fd: Arg, push_back: bool) {
319        let redir = RedirType::OutTrunc(dest_fd, RedirArg::Fd(source_fd));
320        if push_back {
321            self.redir_stack.push(redir);
322        } else {
323            self.redir_stack.insert(0, redir);
324        }
325    }
326
327    /// Push a fd to fd redirect onto the stack.
328    pub fn set_in_out_fd(&mut self, dest_fd: FileDesc, source_fd: Arg) {
329        let redir = RedirType::InOut(dest_fd, RedirArg::Fd(source_fd));
330        self.redir_stack.push(redir);
331    }
332
333    /// Push an input file path to the redirect stack for fd.
334    pub fn set_in_out_path(&mut self, dest_fd: FileDesc, path: Arg) {
335        let redir = RedirType::InOut(dest_fd, RedirArg::Path(path));
336        self.redir_stack.push(redir);
337    }
338
339    /// Push an input file path to the redirect stack for fd.
340    pub fn set_in_path(&mut self, dest_fd: FileDesc, path: Arg) {
341        let redir = RedirType::In(dest_fd, RedirArg::Path(path));
342        self.redir_stack.push(redir);
343    }
344
345    /// Push input data to the redirect stack for fd.
346    pub fn set_in_direct(&mut self, dest_fd: FileDesc, data: Arg) {
347        let redir = RedirType::InDirect(dest_fd, data);
348        self.redir_stack.push(redir);
349    }
350
351    /// Push an output file path to the redirect stack for fd.
352    pub fn set_out_path(&mut self, dest_fd: FileDesc, path: Arg, overwrite: bool) {
353        let redir = if overwrite {
354            RedirType::OutTrunc(dest_fd, RedirArg::Path(path))
355        } else {
356            RedirType::Out(dest_fd, RedirArg::Path(path))
357        };
358        self.redir_stack.push(redir);
359    }
360
361    /// Clear the redirect stack.
362    pub fn clear(&mut self) {
363        self.redir_stack.clear();
364    }
365
366    /// Extend this redir stack with the redirs from other.
367    pub fn extend(&mut self, other: &Redirects) {
368        self.redir_stack.extend(other.redir_stack.iter().cloned());
369    }
370
371    fn collect_internal_fds(&self, fd_set: &mut HashSet<FileDesc>) {
372        for r in &self.redir_stack {
373            match r {
374                RedirType::In(_, RedirArg::InternalFd(fd)) => {
375                    fd_set.insert(*fd);
376                }
377                RedirType::In(_, _) => {}
378                RedirType::InDirect(_, _) => {}
379                RedirType::Out(_, RedirArg::InternalFd(fd)) => {
380                    fd_set.insert(*fd);
381                }
382                RedirType::Out(_, _) => {}
383                RedirType::OutTrunc(_, RedirArg::InternalFd(fd)) => {
384                    fd_set.insert(*fd);
385                }
386                RedirType::OutTrunc(_, _) => {}
387                RedirType::InOut(_, RedirArg::InternalFd(fd)) => {
388                    fd_set.insert(*fd);
389                }
390                RedirType::InOut(_, _) => {}
391            }
392        }
393    }
394
395    fn fds_to_internal(&mut self, fd_set: &HashSet<FileDesc>) {
396        for r in self.redir_stack.iter_mut() {
397            match r {
398                RedirType::In(ifd, RedirArg::Fd(fd_arg)) => {
399                    if let Ok(fd) = fd_arg.try_into() {
400                        if fd_set.contains(&fd) {
401                            *r = RedirType::In(*ifd, RedirArg::InternalFd(fd));
402                        }
403                    }
404                }
405                RedirType::In(_, _) => {}
406                RedirType::InDirect(_, _) => {}
407                RedirType::Out(ofd, RedirArg::Fd(fd_arg)) => {
408                    if let Ok(fd) = fd_arg.try_into() {
409                        if fd_set.contains(&fd) {
410                            *r = RedirType::Out(*ofd, RedirArg::InternalFd(fd));
411                        }
412                    }
413                }
414                RedirType::Out(_, _) => {}
415                RedirType::OutTrunc(ofd, RedirArg::Fd(fd_arg)) => {
416                    if let Ok(fd) = fd_arg.try_into() {
417                        if fd_set.contains(&fd) {
418                            *r = RedirType::OutTrunc(*ofd, RedirArg::InternalFd(fd));
419                        }
420                    }
421                }
422                RedirType::OutTrunc(_, _) => {}
423                RedirType::InOut(iofd, RedirArg::Fd(fd_arg)) => {
424                    if let Ok(fd) = fd_arg.try_into() {
425                        if fd_set.contains(&fd) {
426                            *r = RedirType::InOut(*iofd, RedirArg::InternalFd(fd));
427                        }
428                    }
429                }
430                RedirType::InOut(_, _) => {}
431            }
432        }
433    }
434}
435
436impl Default for Redirects {
437    fn default() -> Self {
438        Self::new()
439    }
440}
441
442/// An individual command with args.
443#[derive(Clone, Debug)]
444pub struct CommandWithArgs {
445    #[allow(rustdoc::broken_intra_doc_links)]
446    /// args[0] is the command.
447    args: Vec<Arg>,
448    stdios: Option<Redirects>,
449}
450
451impl Display for CommandWithArgs {
452    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
453        let mut first = true;
454        for arg in &self.args {
455            if first {
456                write!(f, "{arg}")?;
457                first = false;
458            } else {
459                write!(f, " {arg}")?;
460            }
461        }
462        if let Some(ios) = &self.stdios {
463            write!(f, "{ios}")?;
464        }
465        Ok(())
466    }
467}
468
469impl CommandWithArgs {
470    /// Create a new empty command and args.
471    pub fn new() -> Self {
472        Self {
473            args: vec![],
474            stdios: None,
475        }
476    }
477
478    /// Push a new arg onto the command, the first "arg" is the command itself.
479    pub fn push_arg(&mut self, arg: Arg) {
480        self.args.push(arg);
481    }
482
483    /// Push a new env var arg onto the command, the first "arg" is the command itself.
484    pub fn push_env_var_arg(&mut self, arg: OsString) {
485        self.args.push(Arg::Var(arg));
486    }
487
488    /// Push a new env var arg onto the command, the first "arg" is the command itself.
489    pub fn push_run_arg(&mut self, run: Run) {
490        self.args.push(Arg::Command(run));
491    }
492
493    /// Empty, not even the command is set.
494    pub fn is_empty(&self) -> bool {
495        self.args.is_empty()
496    }
497
498    /// Command name, None if no command name set (args are empty).
499    pub fn command(&self, jobs: &mut Jobs) -> Option<io::Result<OsString>> {
500        self.args.first().map(|v| v.resolve_arg(jobs))
501    }
502
503    /// Args to the command.
504    pub fn args(&self) -> &[Arg] {
505        if self.args.is_empty() {
506            &self.args[..]
507        } else {
508            &self.args[1..]
509        }
510    }
511
512    /// Iterator over the arguments for the command.
513    pub fn args_iter(&self) -> CommandArgs {
514        CommandArgs {
515            args: self.args(),
516            index: 0,
517        }
518    }
519
520    /// Set the stdio redirect stack for this command.
521    pub fn set_stdios(&mut self, stdios: Redirects) {
522        self.stdios = Some(stdios);
523    }
524
525    /// Set the stdio redirect stack for this command.
526    pub fn stdios(&self) -> &Option<Redirects> {
527        &self.stdios
528    }
529
530    /// Extend the redirect stack for this command with stdio.
531    pub fn extend_stdios(&mut self, stdios: &Redirects) {
532        let mut current_stdios = self.stdios.take().unwrap_or_default();
533        current_stdios.extend(stdios);
534        self.stdios = Some(current_stdios);
535    }
536
537    /// If fd is Some value then put it at the front of the redir queue for this command.
538    pub fn push_stdin_front(&mut self, fd: Option<FileDesc>) {
539        if let Some(fd) = fd {
540            if let Some(stdios) = self.stdios.as_mut() {
541                stdios.set_in_internal_fd(STDIN_FILENO, fd, false);
542            } else {
543                let mut stdios = Redirects::default();
544                stdios.set_in_internal_fd(STDIN_FILENO, fd, true);
545                self.stdios = Some(stdios);
546            }
547        }
548    }
549
550    /// If fd is Some value then put it at the front of the redir queue for this command.
551    pub fn push_stdout_front(&mut self, fd: Option<FileDesc>) {
552        if let Some(fd) = fd {
553            if let Some(stdios) = self.stdios.as_mut() {
554                stdios.set_out_internal_fd(STDOUT_FILENO, fd, false);
555            } else {
556                let mut stdios = Redirects::default();
557                stdios.set_out_internal_fd(STDOUT_FILENO, fd, true);
558                self.stdios = Some(stdios);
559            }
560        }
561    }
562
563    /// Process redirects.
564    pub fn process_redirects(&self, jobs: &mut Jobs) -> Result<HashSet<FileDesc>, io::Error> {
565        if let Some(redirects) = &self.stdios {
566            redirects.process(jobs)
567        } else {
568            Ok(HashSet::new())
569        }
570    }
571
572    fn collect_internal_fds(&self, fd_set: &mut HashSet<FileDesc>) {
573        if let Some(stdios) = &self.stdios {
574            stdios.collect_internal_fds(fd_set);
575        }
576    }
577
578    pub fn fds_to_internal(&mut self, fd_set: &HashSet<FileDesc>) {
579        if let Some(stdios) = &mut self.stdios {
580            stdios.fds_to_internal(fd_set);
581        }
582    }
583
584    /// Return a set of all the 'internal' file descriptors (for pipes etc).
585    pub fn get_internal_fds(&self) -> HashSet<FileDesc> {
586        let mut res = HashSet::new();
587        self.collect_internal_fds(&mut res);
588        res
589    }
590}
591
592impl Default for CommandWithArgs {
593    fn default() -> Self {
594        Self::new()
595    }
596}
597
598pub struct CommandArgs<'args> {
599    args: &'args [Arg],
600    index: usize,
601}
602
603impl<'args> Iterator for CommandArgs<'args> {
604    type Item = &'args Arg;
605
606    fn next(&mut self) -> Option<Self::Item> {
607        if self.index < self.args.len() {
608            let r = &self.args[self.index];
609            self.index += 1;
610            Some(r)
611        } else {
612            None
613        }
614    }
615}
616
617/// Command(s) ready to run with context.
618#[derive(Clone, Debug)]
619pub enum Run {
620    Command(CommandWithArgs),
621    BackgroundCommand(CommandWithArgs),
622    Pipe(Vec<Run>),
623    Sequence(Vec<Run>),
624    And(Vec<Run>),
625    Or(Vec<Run>),
626    Subshell(Box<Run>),
627    Empty,
628}
629
630fn write_seq(f: &mut Formatter<'_>, seq: &[Run], sep: &str) -> std::fmt::Result {
631    let mut first = true;
632    for s in seq {
633        if first {
634            write!(f, "{s}")?;
635            first = false;
636        } else {
637            write!(f, " {sep} {s}")?;
638        }
639    }
640    Ok(())
641}
642
643impl Display for Run {
644    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
645        match self {
646            Self::Command(command) => write!(f, "{command}")?,
647            Self::BackgroundCommand(command) => write!(f, "{command} &")?,
648            Self::Pipe(seq) => write_seq(f, &seq[..], "|")?,
649            Self::Sequence(seq) => write_seq(f, &seq[..], ";")?,
650            Self::And(seq) => write_seq(f, &seq[..], "&&")?,
651            Self::Or(seq) => write_seq(f, &seq[..], "||")?,
652            Self::Subshell(sub_run) => write!(f, "({sub_run})")?,
653            Self::Empty => {}
654        }
655        Ok(())
656    }
657}
658
659impl Run {
660    /// Push a new Run onto the Run.  If it is not the first command will add to or create a sequence.
661    pub fn push_run(self, new_run: Run) -> Self {
662        match self {
663            Run::Command(current) => Run::Sequence(vec![Run::Command(current), new_run]),
664            Run::BackgroundCommand(current) => {
665                Run::Sequence(vec![Run::BackgroundCommand(current), new_run])
666            }
667            Run::Pipe(pipe) => Run::Sequence(vec![Run::Pipe(pipe), new_run]),
668            Run::Sequence(mut seq) => {
669                seq.push(new_run);
670                Run::Sequence(seq)
671            }
672            Run::And(seq) => Run::Sequence(vec![Run::And(seq), new_run]),
673            Run::Or(seq) => Run::Sequence(vec![Run::Or(seq), new_run]),
674            Run::Subshell(current) => Run::Sequence(vec![Run::Subshell(current), new_run]),
675            Run::Empty => new_run,
676        }
677    }
678
679    /// Push Run onto an existing or create a new pipe sequence.
680    pub fn push_pipe(self, new_run: Run) -> Self {
681        match self {
682            Run::Command(current) => Run::Pipe(vec![Run::Command(current), new_run]),
683            Run::BackgroundCommand(current) => {
684                Run::Pipe(vec![Run::BackgroundCommand(current), new_run])
685            }
686            Run::Pipe(mut pipe) => {
687                pipe.push(new_run);
688                Run::Pipe(pipe)
689            }
690            Run::Sequence(seq) => Run::Pipe(vec![Run::Sequence(seq), new_run]),
691            Run::And(seq) => Run::Pipe(vec![Run::And(seq), new_run]),
692            Run::Or(seq) => Run::Pipe(vec![Run::Or(seq), new_run]),
693            Run::Subshell(current) => Run::Pipe(vec![Run::Subshell(current), new_run]),
694            Run::Empty => new_run,
695        }
696    }
697
698    /// Push new Run onto an existing or create a new sequence.
699    pub fn push_sequence(self, new_run: Run) -> Self {
700        match self {
701            Run::Command(current) => Run::Sequence(vec![Run::Command(current), new_run]),
702            Run::BackgroundCommand(current) => {
703                Run::Sequence(vec![Run::BackgroundCommand(current), new_run])
704            }
705            Run::Pipe(pipe) => Run::Sequence(vec![Run::Pipe(pipe), new_run]),
706            Run::Sequence(mut seq) => {
707                seq.push(new_run);
708                Run::Sequence(seq)
709            }
710            Run::And(seq) => Run::Sequence(vec![Run::And(seq), new_run]),
711            Run::Or(seq) => Run::Sequence(vec![Run::Or(seq), new_run]),
712            Run::Subshell(current) => Run::Sequence(vec![Run::Subshell(current), new_run]),
713            Run::Empty => new_run,
714        }
715    }
716
717    /// Push new Run onto an existing or create a new AND sequence.
718    pub fn push_and(self, new_run: Run) -> Self {
719        match self {
720            Run::Command(current) => Run::And(vec![Run::Command(current), new_run]),
721            Run::BackgroundCommand(current) => {
722                Run::And(vec![Run::BackgroundCommand(current), new_run])
723            }
724            Run::Pipe(pipe) => Run::And(vec![Run::Pipe(pipe), new_run]),
725            Run::Sequence(seq) => Run::And(vec![Run::Sequence(seq), new_run]),
726            Run::And(mut seq) => {
727                seq.push(new_run);
728                Run::And(seq)
729            }
730            Run::Or(seq) => Run::And(vec![Run::Or(seq), new_run]),
731            Run::Subshell(current) => Run::And(vec![Run::Subshell(current), new_run]),
732            Run::Empty => new_run,
733        }
734    }
735
736    /// Push new Run onto an existing or create a new OR sequence.
737    pub fn push_or(self, new_run: Run) -> Self {
738        match self {
739            Run::Command(current) => Run::Or(vec![Run::Command(current), new_run]),
740            Run::BackgroundCommand(current) => {
741                Run::Or(vec![Run::BackgroundCommand(current), new_run])
742            }
743            Run::Pipe(pipe) => Run::Or(vec![Run::Pipe(pipe), new_run]),
744            Run::Sequence(seq) => Run::Or(vec![Run::Or(seq), new_run]),
745            Run::And(seq) => Run::Or(vec![Run::And(seq), new_run]),
746            Run::Or(mut seq) => {
747                seq.push(new_run);
748                Run::Or(seq)
749            }
750            Run::Subshell(current) => Run::Or(vec![Run::Subshell(current), new_run]),
751            Run::Empty => new_run,
752        }
753    }
754
755    /// If fd is Some value then put it at the front of the redir queue for the first command in the Run.
756    pub fn push_stdin_front(&mut self, fd: Option<FileDesc>) {
757        if let Some(fd) = fd {
758            match self {
759                Run::Command(current) => current.push_stdin_front(Some(fd)),
760                Run::BackgroundCommand(current) => current.push_stdin_front(Some(fd)),
761                Run::Pipe(seq) | Run::Sequence(seq) | Run::And(seq) | Run::Or(seq) => {
762                    if let Some(run) = seq.first_mut() {
763                        run.push_stdin_front(Some(fd));
764                    }
765                }
766                Run::Subshell(current) => current.push_stdin_front(Some(fd)),
767                Run::Empty => {}
768            }
769        }
770    }
771
772    /// If fd is Some value then put it at the front of the redir queue for the last command in the Run.
773    pub fn push_stdout_front(&mut self, fd: Option<FileDesc>) {
774        if let Some(fd) = fd {
775            match self {
776                Run::Command(current) => current.push_stdout_front(Some(fd)),
777                Run::BackgroundCommand(current) => current.push_stdout_front(Some(fd)),
778                Run::Pipe(seq) | Run::Sequence(seq) | Run::And(seq) | Run::Or(seq) => {
779                    if let Some(run) = seq.last_mut() {
780                        run.push_stdout_front(Some(fd));
781                    }
782                }
783                Run::Subshell(current) => current.push_stdout_front(Some(fd)),
784                Run::Empty => {}
785            }
786        }
787    }
788
789    fn collect_internal_fds(&self, fd_set: &mut HashSet<FileDesc>) {
790        match self {
791            Run::Command(current) => current.collect_internal_fds(fd_set),
792            Run::BackgroundCommand(current) => current.collect_internal_fds(fd_set),
793            Run::Pipe(seq) | Run::Sequence(seq) | Run::And(seq) | Run::Or(seq) => {
794                for run in seq {
795                    run.collect_internal_fds(fd_set);
796                }
797            }
798            Run::Subshell(current) => current.collect_internal_fds(fd_set),
799            Run::Empty => {}
800        }
801    }
802
803    pub fn fds_to_internal(&mut self, fd_set: &HashSet<FileDesc>) {
804        match self {
805            Run::Command(current) => current.fds_to_internal(fd_set),
806            Run::BackgroundCommand(current) => current.fds_to_internal(fd_set),
807            Run::Pipe(seq) | Run::Sequence(seq) | Run::And(seq) | Run::Or(seq) => {
808                for run in seq {
809                    run.fds_to_internal(fd_set);
810                }
811            }
812            Run::Subshell(current) => current.fds_to_internal(fd_set),
813            Run::Empty => {}
814        }
815    }
816
817    /// Return a set of all the 'internal' file descriptors (for pipes etc).
818    pub fn get_internal_fds(&self) -> HashSet<FileDesc> {
819        let mut res = HashSet::new();
820        self.collect_internal_fds(&mut res);
821        res
822    }
823
824    /// If fd is Some value then put it at the front of the redir queue for the last command in the Run.
825    pub fn push_arg_end(&mut self, arg: Arg) {
826        match self {
827            Run::Command(current) => current.push_arg(arg),
828            Run::BackgroundCommand(current) => current.push_arg(arg),
829            Run::Pipe(seq) | Run::Sequence(seq) | Run::And(seq) | Run::Or(seq) => {
830                if let Some(run) = seq.last_mut() {
831                    run.push_arg_end(arg);
832                }
833            }
834            Run::Subshell(current) => current.push_arg_end(arg),
835            Run::Empty => {}
836        }
837    }
838
839    /// If fd is Some value then put it at the front of the redir queue for the last command in the Run.
840    pub fn extend_redirs_end(&mut self, redirs: &Redirects) {
841        match self {
842            Run::Command(current) => current.extend_stdios(redirs),
843            Run::BackgroundCommand(current) => current.extend_stdios(redirs),
844            Run::Pipe(seq) | Run::Sequence(seq) | Run::And(seq) | Run::Or(seq) => {
845                if let Some(run) = seq.last_mut() {
846                    run.extend_redirs_end(redirs);
847                }
848            }
849            Run::Subshell(current) => current.extend_redirs_end(redirs),
850            Run::Empty => {}
851        }
852    }
853}