1 /*
2  *  Created by Martin on 28/04/2018.
3  *
4  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
5  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  */
7 
8 #include "catch_output_redirect.h"
9 #include "catch_enforce.h"
10 
11 #include <cstdio>
12 #include <cstring>
13 #include <fstream>
14 #include <sstream>
15 #include <stdexcept>
16 
17 #if defined(CATCH_CONFIG_NEW_CAPTURE)
18     #if defined(_MSC_VER)
19     #include <io.h>      //_dup and _dup2
20     #define dup _dup
21     #define dup2 _dup2
22     #define fileno _fileno
23     #else
24     #include <unistd.h>  // dup and dup2
25     #endif
26 #endif
27 
28 
29 namespace Catch {
30 
RedirectedStream(std::ostream & originalStream,std::ostream & redirectionStream)31     RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
32     :   m_originalStream( originalStream ),
33         m_redirectionStream( redirectionStream ),
34         m_prevBuf( m_originalStream.rdbuf() )
35     {
36         m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
37     }
38 
~RedirectedStream()39     RedirectedStream::~RedirectedStream() {
40         m_originalStream.rdbuf( m_prevBuf );
41     }
42 
RedirectedStdOut()43     RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
str() const44     auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
45 
RedirectedStdErr()46     RedirectedStdErr::RedirectedStdErr()
47     :   m_cerr( Catch::cerr(), m_rss.get() ),
48         m_clog( Catch::clog(), m_rss.get() )
49     {}
str() const50     auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
51 
RedirectedStreams(std::string & redirectedCout,std::string & redirectedCerr)52     RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
53     :   m_redirectedCout(redirectedCout),
54         m_redirectedCerr(redirectedCerr)
55     {}
56 
~RedirectedStreams()57     RedirectedStreams::~RedirectedStreams() {
58         m_redirectedCout += m_redirectedStdOut.str();
59         m_redirectedCerr += m_redirectedStdErr.str();
60     }
61 
62 #if defined(CATCH_CONFIG_NEW_CAPTURE)
63 
64 #if defined(_MSC_VER)
TempFile()65     TempFile::TempFile() {
66         if (tmpnam_s(m_buffer)) {
67             CATCH_RUNTIME_ERROR("Could not get a temp filename");
68         }
69         if (fopen_s(&m_file, m_buffer, "w")) {
70             char buffer[100];
71             if (strerror_s(buffer, errno)) {
72                 CATCH_RUNTIME_ERROR("Could not translate errno to a string");
73             }
74             CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
75         }
76     }
77 #else
TempFile()78     TempFile::TempFile() {
79         m_file = std::tmpfile();
80         if (!m_file) {
81             CATCH_RUNTIME_ERROR("Could not create a temp file.");
82         }
83     }
84 
85 #endif
86 
~TempFile()87     TempFile::~TempFile() {
88          // TBD: What to do about errors here?
89          std::fclose(m_file);
90          // We manually create the file on Windows only, on Linux
91          // it will be autodeleted
92 #if defined(_MSC_VER)
93          std::remove(m_buffer);
94 #endif
95     }
96 
97 
getFile()98     FILE* TempFile::getFile() {
99         return m_file;
100     }
101 
getContents()102     std::string TempFile::getContents() {
103         std::stringstream sstr;
104         char buffer[100] = {};
105         std::rewind(m_file);
106         while (std::fgets(buffer, sizeof(buffer), m_file)) {
107             sstr << buffer;
108         }
109         return sstr.str();
110     }
111 
OutputRedirect(std::string & stdout_dest,std::string & stderr_dest)112     OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
113         m_originalStdout(dup(1)),
114         m_originalStderr(dup(2)),
115         m_stdoutDest(stdout_dest),
116         m_stderrDest(stderr_dest) {
117         dup2(fileno(m_stdoutFile.getFile()), 1);
118         dup2(fileno(m_stderrFile.getFile()), 2);
119     }
120 
~OutputRedirect()121     OutputRedirect::~OutputRedirect() {
122         Catch::cout() << std::flush;
123         fflush(stdout);
124         // Since we support overriding these streams, we flush cerr
125         // even though std::cerr is unbuffered
126         Catch::cerr() << std::flush;
127         Catch::clog() << std::flush;
128         fflush(stderr);
129 
130         dup2(m_originalStdout, 1);
131         dup2(m_originalStderr, 2);
132 
133         m_stdoutDest += m_stdoutFile.getContents();
134         m_stderrDest += m_stderrFile.getContents();
135     }
136 
137 #endif // CATCH_CONFIG_NEW_CAPTURE
138 
139 } // namespace Catch
140 
141 #if defined(CATCH_CONFIG_NEW_CAPTURE)
142     #if defined(_MSC_VER)
143     #undef dup
144     #undef dup2
145     #undef fileno
146     #endif
147 #endif
148