@@ -2,18 +2,22 @@ use either::Either;
22use libc;
33use glob:: { self , MatchOptions } ;
44use glob:: Pattern as GlobPattern ;
5+ use nix:: unistd;
56
67use std:: borrow:: Cow ;
78use std:: cell:: RefCell ;
89use std:: ffi:: { OsString , OsStr } ;
10+ use std:: fmt;
911use std:: fs:: { File , OpenOptions } ;
10- use std:: io:: Write ;
12+ use std:: io:: { Read , Write } ;
1113use std:: iter:: FromIterator ;
12- use std:: os:: unix:: ffi:: OsStrExt ;
14+ use std:: os:: unix:: ffi:: { OsStringExt , OsStrExt } ;
1315use std:: os:: unix:: io:: RawFd ;
1416use std:: process;
1517use std:: rc:: Rc ;
18+ use std:: result:: Result as StdResult ;
1619
20+ use util:: RawFdWrapper ;
1721use super :: { NAME , UtilSetup } ;
1822use super :: command:: { CommandEnv , CommandWrapper , ExecData , ExecEnv , InProcessCommand } ;
1923use 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