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::__anon325bcd660111::NoColourImpl37             void use( Colour::Code ) {}
38 
instanceCatch::__anon325bcd660111::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         virtual 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         virtual 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             Catch::cout() << '\033' << _escapeCode;
164         }
165     };
166 
useColourOnPlatform()167     bool useColourOnPlatform() {
168         return
169 #ifdef CATCH_PLATFORM_MAC
170             !isDebuggerActive() &&
171 #endif
172 #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
173             isatty(STDOUT_FILENO)
174 #else
175             false
176 #endif
177             ;
178     }
platformColourInstance()179     IColourImpl* platformColourInstance() {
180         ErrnoGuard guard;
181         IConfigPtr config = getCurrentContext().getConfig();
182         UseColour::YesOrNo colourMode = config
183             ? config->useColour()
184             : UseColour::Auto;
185         if( colourMode == UseColour::Auto )
186             colourMode = useColourOnPlatform()
187                 ? UseColour::Yes
188                 : UseColour::No;
189         return colourMode == UseColour::Yes
190             ? PosixColourImpl::instance()
191             : NoColourImpl::instance();
192     }
193 
194 } // end anon namespace
195 } // end namespace Catch
196 
197 #else  // not Windows or ANSI ///////////////////////////////////////////////
198 
199 namespace Catch {
200 
platformColourInstance()201     static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
202 
203 } // end namespace Catch
204 
205 #endif // Windows/ ANSI/ None
206 
207 namespace Catch {
208 
Colour(Code _colourCode)209     Colour::Colour( Code _colourCode ) { use( _colourCode ); }
Colour(Colour && rhs)210     Colour::Colour( Colour&& rhs ) noexcept {
211         m_moved = rhs.m_moved;
212         rhs.m_moved = true;
213     }
operator =(Colour && rhs)214     Colour& Colour::operator=( Colour&& rhs ) noexcept {
215         m_moved = rhs.m_moved;
216         rhs.m_moved  = true;
217         return *this;
218     }
219 
~Colour()220     Colour::~Colour(){ if( !m_moved ) use( None ); }
221 
use(Code _colourCode)222     void Colour::use( Code _colourCode ) {
223         static IColourImpl* impl = platformColourInstance();
224         impl->use( _colourCode );
225     }
226 
operator <<(std::ostream & os,Colour const &)227     std::ostream& operator << ( std::ostream& os, Colour const& ) {
228         return os;
229     }
230 
231 } // end namespace Catch
232 
233 #if defined(__clang__)
234 #    pragma clang diagnostic pop
235 #endif
236 
237