3232#include < sys/wait.h>
3333#include < sys/resource.h>
3434#include < unistd.h>
35+
36+ #if defined(__APPLE__)
37+ #include < util.h>
38+ #else
39+ #include < pty.h>
40+ #endif
3541#endif
3642
3743#ifdef _WIN32
@@ -680,9 +686,10 @@ namespace vix::commands::RunCommand::detail
680686
681687 LiveRunResult result;
682688
683- int outPipe[2 ] = {-1 , -1 };
689+ int masterFd = -1 ;
690+ int slaveFd = -1 ;
684691
685- if (::pipe (outPipe ) != 0 )
692+ if (::openpty (&masterFd, &slaveFd, nullptr , nullptr , nullptr ) != 0 )
686693 {
687694 const int st = std::system (cmd.c_str ());
688695 result.rawStatus = st;
@@ -693,8 +700,8 @@ namespace vix::commands::RunCommand::detail
693700 pid_t pid = ::fork ();
694701 if (pid < 0 )
695702 {
696- close_safe (outPipe[ 0 ] );
697- close_safe (outPipe[ 1 ] );
703+ close_safe (masterFd );
704+ close_safe (slaveFd );
698705
699706 const int st = std::system (cmd.c_str ());
700707 result.rawStatus = st;
@@ -710,7 +717,9 @@ namespace vix::commands::RunCommand::detail
710717 saChild.sa_flags = 0 ;
711718 ::sigaction (SIGINT, &saChild, nullptr );
712719
713- ::setpgid (0 , 0 );
720+ // Nouvelle session pour détacher le child du terminal parent
721+ // et permettre au PTY esclave de jouer le rôle de terminal.
722+ ::setsid ();
714723
715724 ::setenv (" ASAN_OPTIONS" ,
716725 " abort_on_error=1:"
@@ -727,12 +736,19 @@ namespace vix::commands::RunCommand::detail
727736 " halt_on_error=1:print_stacktrace=1:color=never" ,
728737 1 );
729738
730- ::close (outPipe[0 ]);
739+ ::close (masterFd);
740+
741+ ::dup2 (slaveFd, STDIN_FILENO);
742+ ::dup2 (slaveFd, STDOUT_FILENO);
743+ ::dup2 (slaveFd, STDERR_FILENO);
731744
732- ::dup2 (outPipe[ 1 ], STDOUT_FILENO);
733- ::dup2 (outPipe[ 1 ], STDERR_FILENO );
745+ if (slaveFd > STDERR_FILENO)
746+ ::close (slaveFd );
734747
735- ::close (outPipe[1 ]);
748+ // Avec un PTY, beaucoup de programmes passent naturellement
749+ // en comportement terminal interactif. On garde quand même ceci.
750+ ::setvbuf (stdout, nullptr , _IOLBF, 0 );
751+ ::setvbuf (stderr, nullptr , _IONBF, 0 );
736752
737753 if (::getenv (" VIX_MODE" ) == nullptr )
738754 ::setenv (" VIX_MODE" , " run" , 1 );
@@ -748,7 +764,7 @@ namespace vix::commands::RunCommand::detail
748764 _exit (127 );
749765 }
750766
751- close_safe (outPipe[ 1 ] );
767+ close_safe (slaveFd );
752768 ::setpgid (pid, pid);
753769
754770 const bool useSpinner = !spinnerLabel.empty ();
@@ -922,7 +938,6 @@ namespace vix::commands::RunCommand::detail
922938 auto has = [&](std::string_view s) noexcept
923939 { return line.find (s) != std::string_view::npos; };
924940
925- // libstdc++ / libc++ terminate noise
926941 if (has (" terminate called after throwing an instance of" ))
927942 return true ;
928943 if (has (" terminating with uncaught exception" ))
@@ -931,13 +946,8 @@ namespace vix::commands::RunCommand::detail
931946 return true ;
932947 if (has (" std::terminate" ))
933948 return true ;
934-
935- // the missing line in your output
936- // libstdc++ prints: " what(): Weird!"
937949 if (has (" what():" ))
938950 return true ;
939-
940- // common endings (optional)
941951 if (has (" Aborted (core dumped)" ))
942952 return true ;
943953 if (has (" core dumped" ))
@@ -952,18 +962,15 @@ namespace vix::commands::RunCommand::detail
952962 {
953963 for (char c : s)
954964 {
955- // ignore line endings
956965 if (c == ' \n ' || c == ' \r ' )
957966 continue ;
958967
959- // any non-space/tab means it's not whitespace-only
960968 if (c != ' ' && c != ' \t ' )
961969 return false ;
962970 }
963971 return true ;
964972 }
965973
966- // Remove noise from what we PRINT, but keep it in result.stdoutText capture.
967974 std::string filter_for_print (const std::string &chunk)
968975 {
969976 std::string data = carry;
@@ -1002,6 +1009,32 @@ namespace vix::commands::RunCommand::detail
10021009 SanitizerSuppressor sanitizer;
10031010 UncaughtExceptionSuppressor uncaught;
10041011
1012+ auto read_from_pty = [](int fd, std::string &outChunk) -> bool
1013+ {
1014+ char buf[4096 ];
1015+ const ssize_t n = ::read (fd, buf, sizeof (buf));
1016+ if (n > 0 )
1017+ {
1018+ outChunk.assign (buf, static_cast <std::size_t >(n));
1019+ return true ;
1020+ }
1021+
1022+ outChunk.clear ();
1023+
1024+ // Sur PTY, quand le slave se ferme, read() peut retourner
1025+ // 0 ou -1 avec errno = EIO. On traite les deux comme EOF.
1026+ if (n == 0 )
1027+ return false ;
1028+
1029+ if (n < 0 && errno == EINTR)
1030+ return true ;
1031+
1032+ if (n < 0 && errno == EIO)
1033+ return false ;
1034+
1035+ return false ;
1036+ };
1037+
10051038 bool running = true ;
10061039 bool printedSomething = false ;
10071040 bool printedRealOutput = false ;
@@ -1132,10 +1165,10 @@ namespace vix::commands::RunCommand::detail
11321165 FD_ZERO (&fds);
11331166
11341167 int maxfd = -1 ;
1135- if (outPipe[ 0 ] >= 0 )
1168+ if (masterFd >= 0 )
11361169 {
1137- FD_SET (outPipe[ 0 ] , &fds);
1138- maxfd = outPipe[ 0 ] ;
1170+ FD_SET (masterFd , &fds);
1171+ maxfd = masterFd ;
11391172 }
11401173
11411174 if (maxfd < 0 )
@@ -1183,58 +1216,61 @@ namespace vix::commands::RunCommand::detail
11831216 spinnerActive = false ;
11841217 }
11851218
1186- if (outPipe[ 0 ] >= 0 && FD_ISSET (outPipe[ 0 ] , &fds))
1219+ if (masterFd >= 0 && FD_ISSET (masterFd , &fds))
11871220 {
11881221 std::string chunk;
1189- if (read_into (outPipe[ 0 ] , chunk))
1222+ if (read_from_pty (masterFd , chunk))
11901223 {
1191- result.stdoutText += chunk;
1192-
1193- if (!suppress_known_failure_output && is_known_runtime_port_in_use (chunk))
1194- suppress_known_failure_output = true ;
1224+ if (!chunk.empty ())
1225+ {
1226+ result.stdoutText += chunk;
11951227
1196- if (suppress_known_failure_output)
1197- continue ;
1228+ if (! suppress_known_failure_output && is_known_runtime_port_in_use (chunk) )
1229+ suppress_known_failure_output = true ;
11981230
1199- if (!should_drop_chunk_default (chunk))
1200- {
1201- std::string printable = chunk;
1231+ if (suppress_known_failure_output)
1232+ continue ;
12021233
1203- if (cmakeConfigure)
1204- printable = cmakeNoise.filter (printable);
1234+ if (!should_drop_chunk_default (chunk))
1235+ {
1236+ std::string printable = chunk;
12051237
1206- if (!printable. empty () )
1207- printable = sanitizer. filter_for_print (printable);
1238+ if (cmakeConfigure )
1239+ printable = cmakeNoise. filter (printable);
12081240
1209- if (!printable.empty ())
1210- printable = uncaught .filter_for_print (printable);
1241+ if (!printable.empty ())
1242+ printable = sanitizer .filter_for_print (printable);
12111243
1212- if (!printable.empty ())
1213- {
1214- std::string filtered =
1215- passthroughRuntime ? printable : runtimeFilter.process (printable);
1244+ if (!printable.empty ())
1245+ printable = uncaught.filter_for_print (printable);
12161246
1217- if (!filtered .empty ())
1247+ if (!printable .empty ())
12181248 {
1219- std::string toPrint = drop_vix_error_tip_lines (filtered);
1220- if (!toPrint.empty ())
1221- toPrint = drop_sanitizer_abort_banner_lines (toPrint);
1249+ std::string filtered =
1250+ passthroughRuntime ? printable : runtimeFilter.process (printable);
12221251
1223- if (!toPrint .empty () && !captureOnly )
1252+ if (!filtered .empty ())
12241253 {
1225- write_all (STDOUT_FILENO, toPrint.data (), toPrint.size ());
1226- printedSomething = true ;
1227- printedRealOutput = true ;
1228- result.printed_live = true ;
1229- lastPrintedChar = toPrint.back ();
1254+ std::string toPrint = drop_vix_error_tip_lines (filtered);
1255+ if (!toPrint.empty ())
1256+ toPrint = drop_sanitizer_abort_banner_lines (toPrint);
1257+
1258+ if (!toPrint.empty () && !captureOnly)
1259+ {
1260+ write_all (STDOUT_FILENO, toPrint.data (), toPrint.size ());
1261+ printedSomething = true ;
1262+ printedRealOutput = true ;
1263+ result.printed_live = true ;
1264+ lastPrintedChar = toPrint.back ();
1265+ }
12301266 }
12311267 }
12321268 }
12331269 }
12341270 }
12351271 else
12361272 {
1237- close_safe (outPipe[ 0 ] );
1273+ close_safe (masterFd );
12381274 }
12391275 }
12401276 }
@@ -1252,7 +1288,7 @@ namespace vix::commands::RunCommand::detail
12521288 if (spinnerActive && !captureOnly)
12531289 spinner_clear (printedSomething, lastPrintedChar);
12541290
1255- close_safe (outPipe[ 0 ] );
1291+ close_safe (masterFd );
12561292
12571293 if (!haveStatus)
12581294 {
@@ -1277,7 +1313,6 @@ namespace vix::commands::RunCommand::detail
12771313 const int sig = WTERMSIG (finalStatus);
12781314 result.terminatedBySignal = true ;
12791315 result.termSignal = sig;
1280-
12811316 result.exitCode = 128 + sig;
12821317 }
12831318 else
0 commit comments