Skip to content

Commit 6f7bf02

Browse files
committed
posix/sh: implement command substitution
1 parent b415087 commit 6f7bf02

File tree

1 file changed

+78
-8
lines changed

1 file changed

+78
-8
lines changed

src/posix/sh/ast.rs

+78-8
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ use either::Either;
22
use libc;
33
use glob::{self, MatchOptions};
44
use glob::Pattern as GlobPattern;
5+
use nix::unistd;
56

67
use std::borrow::Cow;
78
use std::cell::RefCell;
89
use std::ffi::{OsString, OsStr};
10+
use std::fmt;
911
use std::fs::{File, OpenOptions};
10-
use std::io::Write;
12+
use std::io::{Read, Write};
1113
use std::iter::FromIterator;
12-
use std::os::unix::ffi::OsStrExt;
14+
use std::os::unix::ffi::{OsStringExt, OsStrExt};
1315
use std::os::unix::io::RawFd;
1416
use std::process;
1517
use std::rc::Rc;
18+
use std::result::Result as StdResult;
1619

20+
use util::RawFdWrapper;
1721
use super::{NAME, UtilSetup};
1822
use super::command::{CommandEnv, CommandWrapper, ExecData, ExecEnv, InProcessCommand};
1923
use super::env::{EnvFd, Environment};
@@ -1034,12 +1038,61 @@ impl CommandSubst {
10341038
where
10351039
S: UtilSetup,
10361040
{
1037-
// TODO: needs to enter a subshell, so i guess enter_scope()? if so, variables need
1038-
// Locality as well. maybe clone environment?
1039-
// TODO: this needs to use the same spawning mechanism used by piping, so a correct
1040-
// implementation depends on correct piping
1041-
let _ = self.command.execute(setup, env); // just do this for now to make sure stuff works
1042-
unimplemented!()
1041+
// set stdout for the command to an anonymous pipe so we can retrieve the output and return
1042+
// it later (NOTE: we might want to just do this in general if an output EnvFd is
1043+
// EnvFd::Piped, in which case we would just set this EnvFd to EnvFd::Piped instead of
1044+
// manually creating a pipe here)
1045+
let (read, write) = match self.write_error(setup, env, unistd::pipe()) {
1046+
Ok(m) => m,
1047+
Err(f) => return f,
1048+
};
1049+
1050+
// TODO: enter_scope() needs to protect variables somehow, so I guess they need Locality as
1051+
// well? Maybe clone env?
1052+
env.enter_scope();
1053+
1054+
env.set_local_fd(1, EnvFd::Fd(RawFdWrapper::new(write, false, true)));
1055+
let code = self.command.execute(setup, env);
1056+
1057+
env.exit_scope();
1058+
env.special_vars().set_last_exitcode(code);
1059+
1060+
// XXX: not sure if we want to just ignore
1061+
unistd::close(write);
1062+
1063+
// read the output from the pipe into a vector
1064+
let mut output = vec![];
1065+
let res = self.write_error(setup, env, RawFdWrapper::new(read, true, false).read_to_end(&mut output));
1066+
1067+
// XXX: not sure if we want to just ignore these so let them create warnings for now
1068+
unistd::close(read);
1069+
1070+
if let Err(f) = res {
1071+
return f;
1072+
}
1073+
1074+
let size = {
1075+
let mut iter = output.rsplitn(2, |&byte| byte != b'\n');
1076+
let last = iter.next().unwrap();
1077+
if iter.next().is_some() {
1078+
output.len() - last.len()
1079+
} else {
1080+
output.len()
1081+
}
1082+
};
1083+
output.truncate(size);
1084+
OsString::from_vec(output)
1085+
}
1086+
1087+
fn write_error<S, T, U>(&self, setup: &mut S, env: &mut Environment, res: StdResult<T, U>) -> StdResult<T, OsString>
1088+
where
1089+
S: UtilSetup,
1090+
U: fmt::Display,
1091+
{
1092+
write_error(setup, res).map_err(|code| {
1093+
env.special_vars().set_last_exitcode(code);
1094+
OsString::new()
1095+
})
10431096
}
10441097
}
10451098

@@ -1259,3 +1312,20 @@ where
12591312
}
12601313
code
12611314
}
1315+
1316+
fn write_error<S, T, U>(setup: &mut S, result: StdResult<T, U>) -> StdResult<T, ExitCode>
1317+
where
1318+
S: UtilSetup,
1319+
U: fmt::Display,
1320+
{
1321+
match result {
1322+
Ok(m) => Ok(m),
1323+
Err(f) => {
1324+
// FIXME: needs to print out line number
1325+
// XXX: should we ignore any I/O errors?
1326+
let _ = display_msg!(setup.error(), "{}", f);
1327+
// FIXME: this could be different depending on the type of error
1328+
Err(127)
1329+
}
1330+
}
1331+
}

0 commit comments

Comments
 (0)