1 //  (C) Copyright Gennadiy Rozental 2001.
2 //  Distributed under the Boost Software License, Version 1.0.
3 //  (See accompanying file LICENSE_1_0.txt or copy at
4 //  http://www.boost.org/LICENSE_1_0.txt)
5 
6 //  See http://www.boost.org/libs/test for the library home page.
7 //
8 //  File        : $RCSfile$
9 //
10 //  Version     : $Revision$
11 //
12 //  Description : contains definition for setcolor iostream manipulator
13 // ***************************************************************************
14 
15 #ifndef BOOST_TEST_UTILS_SETCOLOR_HPP
16 #define BOOST_TEST_UTILS_SETCOLOR_HPP
17 
18 // Boost.Test
19 #include <boost/test/detail/config.hpp>
20 
21 #include <boost/core/ignore_unused.hpp>
22 
23 // STL
24 #include <iostream>
25 #include <cstdio>
26 #include <cassert>
27 
28 #include <boost/test/detail/suppress_warnings.hpp>
29 
30 #ifdef _WIN32
31   #include <windows.h>
32 
33   #if defined(__MINGW32__) && !defined(COMMON_LVB_UNDERSCORE)
34     // mingw badly mimicking windows.h
35     #define COMMON_LVB_UNDERSCORE 0x8000
36   #endif
37 #endif
38 
39 //____________________________________________________________________________//
40 
41 namespace boost {
42 namespace unit_test {
43 namespace utils {
44 
45 // ************************************************************************** //
46 // **************                    term_attr                 ************** //
47 // ************************************************************************** //
48 
49 struct term_attr { enum _ {
50     NORMAL    = 0,
51     BRIGHT    = 1,
52     DIM       = 2,
53     UNDERLINE = 4,
54     BLINK     = 5,
55     REVERSE   = 7,
56     CROSSOUT  = 9
57 }; };
58 
59 // ************************************************************************** //
60 // **************                   term_color                 ************** //
61 // ************************************************************************** //
62 
63 struct term_color { enum _ {
64     BLACK    = 0,
65     RED      = 1,
66     GREEN    = 2,
67     YELLOW   = 3,
68     BLUE     = 4,
69     MAGENTA  = 5,
70     CYAN     = 6,
71     WHITE    = 7,
72     ORIGINAL = 9
73 }; };
74 
75 // ************************************************************************** //
76 // **************                    setcolor                  ************** //
77 // ************************************************************************** //
78 
79 #ifndef _WIN32
80 class setcolor {
81 public:
82     typedef int state;
83 
84     // Constructor
setcolor(bool is_color_output=false,term_attr::_ attr=term_attr::NORMAL,term_color::_ fg=term_color::ORIGINAL,term_color::_ bg=term_color::ORIGINAL,state * =NULL)85     explicit    setcolor( bool is_color_output = false,
86                           term_attr::_  attr = term_attr::NORMAL,
87                           term_color::_ fg   = term_color::ORIGINAL,
88                           term_color::_ bg   = term_color::ORIGINAL,
89                           state* /* unused */= NULL)
90     : m_is_color_output(is_color_output)
91     {
92         m_command_size = std::sprintf( m_control_command, "%c[%c;3%c;4%cm",
93           0x1B,
94           static_cast<char>(attr + '0'),
95           static_cast<char>(fg + '0'),
96           static_cast<char>(bg + '0'));
97     }
98 
setcolor(bool is_color_output,state *)99     explicit    setcolor(bool is_color_output,
100                          state* /* unused */)
101     : m_is_color_output(is_color_output)
102     {
103         m_command_size = std::sprintf(m_control_command, "%c[%c;3%c;4%cm",
104           0x1B,
105           static_cast<char>(term_attr::NORMAL + '0'),
106           static_cast<char>(term_color::ORIGINAL + '0'),
107           static_cast<char>(term_color::ORIGINAL + '0'));
108     }
109 
110     friend std::ostream&
operator <<(std::ostream & os,setcolor const & sc)111     operator<<( std::ostream& os, setcolor const& sc )
112     {
113        if (sc.m_is_color_output && (&os == &std::cout || &os == &std::cerr)) {
114           return os.write( sc.m_control_command, sc.m_command_size );
115        }
116        return os;
117     }
118 
119 private:
120     // Data members
121     bool        m_is_color_output;
122     char        m_control_command[13];
123     int         m_command_size;
124 };
125 
126 #else
127 
128 class setcolor {
129 
130 protected:
set_console_color(std::ostream & os,WORD * attributes=NULL) const131   void set_console_color(std::ostream& os, WORD *attributes = NULL) const {
132     if (!m_is_color_output || m_state_saved) {
133       return;
134     }
135     DWORD console_type;
136     if (&os == &std::cout) {
137       console_type = STD_OUTPUT_HANDLE;
138     }
139     else if (&os == &std::cerr) {
140       console_type =  STD_ERROR_HANDLE;
141     }
142     else {
143       return;
144     }
145     HANDLE hConsole = GetStdHandle(console_type);
146 
147     if(hConsole == INVALID_HANDLE_VALUE || hConsole == NULL )
148       return;
149 
150     state console_attributes;
151     if(attributes != NULL || (m_restore_state && m_s)) {
152       if (attributes != NULL) {
153         console_attributes = *attributes;
154       }
155       else {
156         console_attributes = *m_s;
157         *m_s = state();
158       }
159       SetConsoleTextAttribute(hConsole, console_attributes);
160       return;
161     }
162 
163     CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
164     GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
165     console_attributes = consoleInfo.wAttributes;
166 
167     if (!m_state_saved && m_s) {
168       assert(!m_restore_state);
169       // we can save the state only the first time this object is used
170       // for modifying the console.
171       *m_s = console_attributes;
172       m_state_saved = true;
173     }
174 
175     WORD fg_attr = 0;
176     switch(m_fg)
177     {
178     case term_color::WHITE:
179       fg_attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
180       break;
181     case term_color::BLACK:
182       fg_attr = 0;
183       break;
184     case term_color::RED:
185       fg_attr = FOREGROUND_RED;
186       break;
187     case term_color::GREEN:
188       fg_attr = FOREGROUND_GREEN;
189       break;
190     case term_color::CYAN:
191       fg_attr = FOREGROUND_GREEN | FOREGROUND_BLUE;
192       break;
193     case term_color::MAGENTA:
194       fg_attr = FOREGROUND_RED | FOREGROUND_BLUE;
195       break;
196     case term_color::BLUE:
197       fg_attr = FOREGROUND_BLUE;
198       break;
199     case term_color::YELLOW:
200       fg_attr = FOREGROUND_RED | FOREGROUND_GREEN;
201       break;
202     case term_color::ORIGINAL:
203     default:
204       fg_attr = console_attributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
205       break;
206     }
207 
208     WORD bg_attr = 0;
209     switch(m_bg)
210     {
211     case term_color::BLACK:
212       bg_attr = 0;
213       break;
214     case term_color::WHITE:
215       bg_attr = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
216       break;
217     case term_color::RED:
218       bg_attr = BACKGROUND_RED;
219       break;
220     case term_color::GREEN:
221       bg_attr = BACKGROUND_GREEN;
222       break;
223     case term_color::BLUE:
224       bg_attr = BACKGROUND_BLUE;
225       break;
226     case term_color::ORIGINAL:
227     default:
228       bg_attr = console_attributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
229       break;
230     }
231 
232     WORD text_attr = 0;
233     switch(m_attr)
234     {
235     case term_attr::BRIGHT:
236       text_attr = FOREGROUND_INTENSITY;
237       break;
238     case term_attr::UNDERLINE:
239       text_attr = COMMON_LVB_UNDERSCORE;
240       break;
241     default:
242       break;
243     }
244 
245     SetConsoleTextAttribute(hConsole, fg_attr | bg_attr | text_attr);
246     return;
247   }
248 
249 public:
250   typedef WORD state;
251 
252   // Constructor
setcolor(bool is_color_output=false,term_attr::_ attr=term_attr::NORMAL,term_color::_ fg=term_color::ORIGINAL,term_color::_ bg=term_color::ORIGINAL,state * s=NULL)253   explicit    setcolor(
254     bool is_color_output = false,
255     term_attr::_  attr = term_attr::NORMAL,
256     term_color::_ fg   = term_color::ORIGINAL,
257     term_color::_ bg   = term_color::ORIGINAL,
258     state* s           = NULL)
259   : m_is_color_output(is_color_output)
260   , m_attr(attr)
261   , m_fg(fg)
262   , m_bg(bg)
263   , m_s(s)
264   , m_restore_state(false)
265   , m_state_saved(false)
266   {}
267 
setcolor(bool is_color_output,state * s)268   explicit    setcolor(
269     bool is_color_output,
270     state* s)
271   : m_is_color_output(is_color_output)
272   , m_attr(term_attr::NORMAL)
273   , m_fg(term_color::ORIGINAL)
274   , m_bg(term_color::ORIGINAL)
275   , m_s(s)
276   , m_restore_state(true)
277   , m_state_saved(false)
278   {}
279 
280   friend std::ostream&
operator <<(std::ostream & os,setcolor const & sc)281     operator<<( std::ostream& os, setcolor const& sc )
282   {
283     sc.set_console_color(os);
284     return os;
285   }
286 
287 private:
288   bool m_is_color_output;
289   term_attr::_ m_attr;
290   term_color::_ m_fg;
291   term_color::_ m_bg;
292   state* m_s;
293   // indicates that the instance has been initialized to restore a previously
294   // stored state
295   bool m_restore_state;
296   // indicates the first time we pull and set the console information.
297   mutable bool m_state_saved;
298 };
299 
300 #endif
301 // ************************************************************************** //
302 // **************                 scope_setcolor               ************** //
303 // ************************************************************************** //
304 
305 struct scope_setcolor {
scope_setcolorboost::unit_test::utils::scope_setcolor306   scope_setcolor()
307   : m_os( 0 )
308   , m_state()
309   , m_is_color_output(false)
310   {}
311 
scope_setcolorboost::unit_test::utils::scope_setcolor312   explicit    scope_setcolor(
313     bool is_color_output,
314     std::ostream& os,
315     term_attr::_  attr = term_attr::NORMAL,
316     term_color::_ fg   = term_color::ORIGINAL,
317     term_color::_ bg   = term_color::ORIGINAL )
318   : m_os( &os )
319   , m_is_color_output(is_color_output)
320   {
321     os << setcolor(is_color_output, attr, fg, bg, &m_state);
322   }
323 
~scope_setcolorboost::unit_test::utils::scope_setcolor324   ~scope_setcolor()
325   {
326     if (m_os) {
327       *m_os << setcolor(m_is_color_output, &m_state);
328     }
329   }
330 private:
331   scope_setcolor(const scope_setcolor& r);
332   scope_setcolor& operator=(const scope_setcolor& r);
333   // Data members
334   std::ostream* m_os;
335   setcolor::state m_state;
336   bool m_is_color_output;
337 };
338 
339 
340 #define BOOST_TEST_SCOPE_SETCOLOR( is_color_output, os, attr, color )               \
341     utils::scope_setcolor const sc(is_color_output, os, utils::attr, utils::color); \
342     boost::ignore_unused( sc )                                                      \
343 /**/
344 
345 } // namespace utils
346 } // namespace unit_test
347 } // namespace boost
348 
349 #include <boost/test/detail/enable_warnings.hpp>
350 
351 #endif // BOOST_TEST_UTILS_SETCOLOR_HPP
352