1 /*
2  *  Created by Phil on 25/2/2012.
3  *  Copyright 2012 Two Blue Cubes Ltd. All rights reserved.
4  *
5  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
6  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 
9 
10 #if defined(__clang__)
11 #    pragma clang diagnostic push
12 #    pragma clang diagnostic ignored "-Wexit-time-destructors"
13 #endif
14 
15 
16 #include "catch_console_colour.h"
17 #include "catch_enforce.h"
18 #include "catch_errno_guard.h"
19 #include "catch_interfaces_config.h"
20 #include "catch_stream.h"
21 #include "catch_context.h"
22 #include "catch_platform.h"
23 #include "catch_debugger.h"
24 #include "catch_windows_h_proxy.h"
25 
26 #include <sstream>
27 
28 namespace Catch {
29     namespace {
30 
31         struct IColourImpl {
32             virtual ~IColourImpl() = default;
33             virtual void use( Colour::Code _colourCode ) = 0;
34         };
35 
36         struct NoColourImpl : IColourImpl {
useCatch::__anon485beded0111::NoColourImpl37             void use( Colour::Code ) {}
38 
instanceCatch::__anon485beded0111::NoColourImpl39             static IColourImpl* instance() {
40                 static NoColourImpl s_instance;
41                 return &s_instance;
42             }
43         };
44 
45     } // anon namespace
46 } // namespace Catch
47 
48 #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
49 #   ifdef CATCH_PLATFORM_WINDOWS
50 #       define CATCH_CONFIG_COLOUR_WINDOWS
51 #   else
52 #       define CATCH_CONFIG_COLOUR_ANSI
53 #   endif
54 #endif
55 
56 
57 #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
58 
59 namespace Catch {
60 namespace {
61 
62     class Win32ColourImpl : public IColourImpl {
63     public:
Win32ColourImpl()64         Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
65         {
66             CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
67             GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
68             originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
69             originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
70         }
71 
use(Colour::Code _colourCode)72         void use( Colour::Code _colourCode ) override {
73             switch( _colourCode ) {
74                 case Colour::None:      return setTextAttribute( originalForegroundAttributes );
75                 case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
76                 case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
77                 case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
78                 case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
79                 case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
80                 case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
81                 case Colour::Grey:      return setTextAttribute( 0 );
82 
83                 case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
84                 case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
85                 case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
86                 case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
87                 case Colour::BrightYellow:  return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
88 
89                 case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
90 
91                 default:
92                     CATCH_ERROR( "Unknown colour requested" );
93             }
94         }
95 
96     private:
setTextAttribute(WORD _textAttribute)97         void setTextAttribute( WORD _textAttribute ) {
98             SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
99         }
100         HANDLE stdoutHandle;
101         WORD originalForegroundAttributes;
102         WORD originalBackgroundAttributes;
103     };
104 
platformColourInstance()105     IColourImpl* platformColourInstance() {
106         static Win32ColourImpl s_instance;
107 
108         IConfigPtr config = getCurrentContext().getConfig();
109         UseColour::YesOrNo colourMode = config
110             ? config->useColour()
111             : UseColour::Auto;
112         if( colourMode == UseColour::Auto )
113             colourMode = UseColour::Yes;
114         return colourMode == UseColour::Yes
115             ? &s_instance
116             : NoColourImpl::instance();
117     }
118 
119 } // end anon namespace
120 } // end namespace Catch
121 
122 #elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
123 
124 #include <unistd.h>
125 
126 namespace Catch {
127 namespace {
128 
129     // use POSIX/ ANSI console terminal codes
130     // Thanks to Adam Strzelecki for original contribution
131     // (http://github.com/nanoant)
132     // https://github.com/philsquared/Catch/pull/131
133     class PosixColourImpl : public IColourImpl {
134     public:
use(Colour::Code _colourCode)135         void use( Colour::Code _colourCode ) override {
136             switch( _colourCode ) {
137                 case Colour::None:
138                 case Colour::White:     return setColour( "[0m" );
139                 case Colour::Red:       return setColour( "[0;31m" );
140                 case Colour::Green:     return setColour( "[0;32m" );
141                 case Colour::Blue:      return setColour( "[0;34m" );
142                 case Colour::Cyan:      return setColour( "[0;36m" );
143                 case Colour::Yellow:    return setColour( "[0;33m" );
144                 case Colour::Grey:      return setColour( "[1;30m" );
145 
146                 case Colour::LightGrey:     return setColour( "[0;37m" );
147                 case Colour::BrightRed:     return setColour( "[1;31m" );
148                 case Colour::BrightGreen:   return setColour( "[1;32m" );
149                 case Colour::BrightWhite:   return setColour( "[1;37m" );
150                 case Colour::BrightYellow:  return setColour( "[1;33m" );
151 
152                 case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
153                 default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
154             }
155         }
instance()156         static IColourImpl* instance() {
157             static PosixColourImpl s_instance;
158             return &s_instance;
159         }
160 
161     private:
setColour(const char * _escapeCode)162         void setColour( const char* _escapeCode ) {
163             getCurrentContext().getConfig()->stream()
164                 << '\033' << _escapeCode;
165         }
166     };
167 
useColourOnPlatform()168     bool useColourOnPlatform() {
169         return
170 #if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
171             !isDebuggerActive() &&
172 #endif
173 #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
174             isatty(STDOUT_FILENO)
175 #else
176             false
177 #endif
178             ;
179     }
platformColourInstance()180     IColourImpl* platformColourInstance() {
181         ErrnoGuard guard;
182         IConfigPtr config = getCurrentContext().getConfig();
183         UseColour::YesOrNo colourMode = config
184             ? config->useColour()
185             : UseColour::Auto;
186         if( colourMode == UseColour::Auto )
187             colourMode = useColourOnPlatform()
188                 ? UseColour::Yes
189                 : UseColour::No;
190         return colourMode == UseColour::Yes
191             ? PosixColourImpl::instance()
192             : NoColourImpl::instance();
193     }
194 
195 } // end anon namespace
196 } // end namespace Catch
197 
198 #else  // not Windows or ANSI ///////////////////////////////////////////////
199 
200 namespace Catch {
201 
platformColourInstance()202     static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
203 
204 } // end namespace Catch
205 
206 #endif // Windows/ ANSI/ None
207 
208 namespace Catch {
209 
Colour(Code _colourCode)210     Colour::Colour( Code _colourCode ) { use( _colourCode ); }
Colour(Colour && rhs)211     Colour::Colour( Colour&& rhs ) noexcept {
212         m_moved = rhs.m_moved;
213         rhs.m_moved = true;
214     }
operator =(Colour && rhs)215     Colour& Colour::operator=( Colour&& rhs ) noexcept {
216         m_moved = rhs.m_moved;
217         rhs.m_moved  = true;
218         return *this;
219     }
220 
~Colour()221     Colour::~Colour(){ if( !m_moved ) use( None ); }
222 
use(Code _colourCode)223     void Colour::use( Code _colourCode ) {
224         static IColourImpl* impl = platformColourInstance();
225         // Strictly speaking, this cannot possibly happen.
226         // However, under some conditions it does happen (see #1626),
227         // and this change is small enough that we can let practicality
228         // triumph over purity in this case.
229         if (impl != NULL) {
230             impl->use( _colourCode );
231         }
232     }
233 
operator <<(std::ostream & os,Colour const &)234     std::ostream& operator << ( std::ostream& os, Colour const& ) {
235         return os;
236     }
237 
238 } // end namespace Catch
239 
240 #if defined(__clang__)
241 #    pragma clang diagnostic pop
242 #endif
243 
244