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#[derive(Clone, Debug)]
13pub enum Arg {
14 Str(OsString),
16 Command(Run),
18 Var(OsString),
20 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#[derive(Clone, Debug)]
104enum RedirArg {
105 Path(Arg),
107 Fd(Arg),
109 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#[derive(Clone, Debug)]
125enum RedirType {
126 In(FileDesc, RedirArg),
128 InDirect(FileDesc, Arg),
130 Out(FileDesc, RedirArg),
132 OutTrunc(FileDesc, RedirArg),
134 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 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 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 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 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#[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 pub fn new() -> Self {
266 Self {
267 redir_stack: Vec::new(),
268 }
269 }
270
271 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 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 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 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 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 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 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 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 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 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 pub fn clear(&mut self) {
363 self.redir_stack.clear();
364 }
365
366 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#[derive(Clone, Debug)]
444pub struct CommandWithArgs {
445 #[allow(rustdoc::broken_intra_doc_links)]
446 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 pub fn new() -> Self {
472 Self {
473 args: vec![],
474 stdios: None,
475 }
476 }
477
478 pub fn push_arg(&mut self, arg: Arg) {
480 self.args.push(arg);
481 }
482
483 pub fn push_env_var_arg(&mut self, arg: OsString) {
485 self.args.push(Arg::Var(arg));
486 }
487
488 pub fn push_run_arg(&mut self, run: Run) {
490 self.args.push(Arg::Command(run));
491 }
492
493 pub fn is_empty(&self) -> bool {
495 self.args.is_empty()
496 }
497
498 pub fn command(&self, jobs: &mut Jobs) -> Option<io::Result<OsString>> {
500 self.args.first().map(|v| v.resolve_arg(jobs))
501 }
502
503 pub fn args(&self) -> &[Arg] {
505 if self.args.is_empty() {
506 &self.args[..]
507 } else {
508 &self.args[1..]
509 }
510 }
511
512 pub fn args_iter(&self) -> CommandArgs {
514 CommandArgs {
515 args: self.args(),
516 index: 0,
517 }
518 }
519
520 pub fn set_stdios(&mut self, stdios: Redirects) {
522 self.stdios = Some(stdios);
523 }
524
525 pub fn stdios(&self) -> &Option<Redirects> {
527 &self.stdios
528 }
529
530 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 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 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 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 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#[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 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 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 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 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 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 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 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 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 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 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}