1 /*
2  * Software License Agreement (BSD License)
3  *
4  *  Copyright (c) 2010, Willow Garage, Inc.
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *
11  *   * Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  *   * Redistributions in binary form must reproduce the above
14  *     copyright notice, this list of conditions and the following
15  *     disclaimer in the documentation and/or other materials provided
16  *     with the distribution.
17  *   * Neither the name of the copyright holder(s) nor the names of its
18  *     contributors may be used to endorse or promote products derived
19  *     from this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  *  POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $Id$
35  *
36  */
37 #include <pcl/console/print.h>
38 #include <algorithm>
39 #include <cstdarg> // for va_list, va_start, va_end
40 #include <cstdlib>
41 #include <cctype> // for toupper
42 #include <map>
43 #include <string>
44 #include <boost/optional.hpp>
45 
46 #if defined _WIN32
47 # include <windows.h>
48 # include <io.h>
49 
50 #ifndef _MSC_VER
51 # define COMMON_LVB_UNDERSCORE    0
52 # define COMMON_LVB_REVERSE_VIDEO 0
53 #endif
54 
55 WORD
convertAttributesColor(int attribute,int fg,int bg=0)56 convertAttributesColor (int attribute, int fg, int bg=0)
57 {
58   static WORD wAttributes[7]  = { 0,                        // TT_RESET
59                                    FOREGROUND_INTENSITY ,    // TT_BRIGHT
60                                    0,                        // TT_DIM
61                                    COMMON_LVB_UNDERSCORE,    // TT_UNDERLINE
62                                    0,                        // TT_BLINK
63                                    COMMON_LVB_REVERSE_VIDEO, // TT_REVERSE
64                                    0                         // TT_HIDDEN
65                                  };
66   static WORD wFgColors[8]  = { 0,                                                  // TT_BLACK
67                                  FOREGROUND_RED,                                     // TT_RED
68                                  FOREGROUND_GREEN ,                                  // TT_GREEN
69                                  FOREGROUND_GREEN | FOREGROUND_RED ,                 // TT_YELLOW
70                                  FOREGROUND_BLUE ,                                   // TT_BLUE
71                                  FOREGROUND_RED | FOREGROUND_BLUE ,                  // TT_MAGENTA
72                                  FOREGROUND_GREEN | FOREGROUND_BLUE,                 // TT_CYAN
73                                  FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED // TT_WHITE
74                                };
75   static WORD wBgColors[8]  = { 0,                                                  // TT_BLACK
76                                  BACKGROUND_RED,                                     // TT_RED
77                                  BACKGROUND_GREEN ,                                  // TT_GREEN
78                                  BACKGROUND_GREEN | BACKGROUND_BLUE ,                // TT_YELLOW
79                                  BACKGROUND_BLUE ,                                   // TT_BLUE
80                                  BACKGROUND_RED | BACKGROUND_BLUE ,                  // TT_MAGENTA
81                                  BACKGROUND_GREEN | BACKGROUND_BLUE,                 // TT_CYAN
82                                  BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED // TT_WHITE
83                                };
84 
85   return wAttributes[attribute] | wFgColors[fg] | wBgColors[bg];
86 }
87 
88 #else
89 #  include <unistd.h>
90 #  include <cstdio>
91 #endif
92 
93 // Map to store, for each output stream, whether to use colored output
94 static std::map<FILE *, boost::optional<bool> > colored_output;
95 
96 ////////////////////////////////////////////////////////////////////////////////
97 inline bool
useColoredOutput(FILE * stream)98 useColoredOutput (FILE *stream)
99 {
100   auto &colored = colored_output[stream];
101   if (!colored)
102   {
103     // Use colored output if PCL_CLICOLOR_FORCE is set or if the output is an interactive terminal
104 #ifdef _WIN32
105     colored = getenv ("PCL_CLICOLOR_FORCE") || _isatty (_fileno (stream));
106 #else
107     colored = getenv ("PCL_CLICOLOR_FORCE") || isatty (fileno (stream));
108 #endif
109   }
110   return colored.get ();
111 }
112 
113 ////////////////////////////////////////////////////////////////////////////////
114 void
enableColoredOutput(FILE * stream,bool enable)115 pcl::console::enableColoredOutput (FILE *stream, bool enable)
116 {
117   colored_output[stream] = enable;
118 }
119 
120 ////////////////////////////////////////////////////////////////////////////////
121 void
change_text_color(FILE * stream,int attribute,int fg,int bg)122 pcl::console::change_text_color (FILE *stream, int attribute, int fg, int bg)
123 {
124   if (!useColoredOutput (stream)) return;
125 
126 #ifdef _WIN32
127   HANDLE h = GetStdHandle ((stream == stdout) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
128   SetConsoleTextAttribute (h, convertAttributesColor (attribute, fg, bg));
129 #else
130   char command[40];
131   // Command is the control command to the terminal
132   sprintf (command, "%c[%d;%d;%dm", 0x1B, attribute, fg + 30, bg + 40);
133   fprintf (stream, "%s", command);
134 #endif
135 }
136 
137 ////////////////////////////////////////////////////////////////////////////////
138 void
change_text_color(FILE * stream,int attribute,int fg)139 pcl::console::change_text_color (FILE *stream, int attribute, int fg)
140 {
141   if (!useColoredOutput (stream)) return;
142 
143 #ifdef _WIN32
144   HANDLE h = GetStdHandle ((stream == stdout) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
145   SetConsoleTextAttribute (h, convertAttributesColor (attribute, fg));
146 #else
147   char command[17];
148   // Command is the control command to the terminal
149   sprintf (command, "%c[%d;%dm", 0x1B, attribute, fg + 30);
150   fprintf (stream, "%s", command);
151 #endif
152 }
153 
154 ////////////////////////////////////////////////////////////////////////////////
155 void
reset_text_color(FILE * stream)156 pcl::console::reset_text_color (FILE *stream)
157 {
158   if (!useColoredOutput (stream)) return;
159 
160 #ifdef _WIN32
161   HANDLE h = GetStdHandle ((stream == stdout) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
162   SetConsoleTextAttribute (h, convertAttributesColor (0, TT_WHITE, TT_BLACK));
163 #else
164   char command[13];
165   // Command is the control command to the terminal
166   sprintf (command, "%c[0;m", 0x1B);
167   fprintf (stream, "%s", command);
168 #endif
169 }
170 
171 ////////////////////////////////////////////////////////////////////////////////
172 void
print_color(FILE * stream,int attr,int fg,const char * format,...)173 pcl::console::print_color (FILE *stream, int attr, int fg, const char *format, ...)
174 {
175   change_text_color (stream, attr, fg);
176   va_list ap;
177 
178   va_start (ap, format);
179   vfprintf (stream, format, ap);
180   va_end (ap);
181 
182   reset_text_color (stream);
183 }
184 
185 ////////////////////////////////////////////////////////////////////////////////
186 void
print_info(const char * format,...)187 pcl::console::print_info (const char *format, ...)
188 {
189   if (!isVerbosityLevelEnabled (L_INFO)) return;
190 
191   reset_text_color (stdout);
192 
193   va_list ap;
194 
195   va_start (ap, format);
196   vfprintf (stdout, format, ap);
197   va_end (ap);
198 }
199 
200 ////////////////////////////////////////////////////////////////////////////////
201 void
print_info(FILE * stream,const char * format,...)202 pcl::console::print_info (FILE *stream, const char *format, ...)
203 {
204   if (!isVerbosityLevelEnabled (L_INFO)) return;
205 
206   reset_text_color (stream);
207 
208   va_list ap;
209 
210   va_start (ap, format);
211   vfprintf (stream, format, ap);
212   va_end (ap);
213 }
214 
215 ////////////////////////////////////////////////////////////////////////////////
216 void
print_highlight(const char * format,...)217 pcl::console::print_highlight (const char *format, ...)
218 {
219   //if (!isVerbosityLevelEnabled (L_ALWAYS)) return;
220 
221   change_text_color (stdout, TT_BRIGHT, TT_GREEN);
222   fprintf (stdout, "> ");
223   reset_text_color (stdout);
224 
225   va_list ap;
226 
227   va_start (ap, format);
228   vfprintf (stdout, format, ap);
229   va_end (ap);
230 }
231 
232 ////////////////////////////////////////////////////////////////////////////////
233 void
print_highlight(FILE * stream,const char * format,...)234 pcl::console::print_highlight (FILE *stream, const char *format, ...)
235 {
236   //if (!isVerbosityLevelEnabled (L_ALWAYS)) return;
237 
238   change_text_color (stream, TT_BRIGHT, TT_GREEN);
239   fprintf (stream, "> ");
240   reset_text_color (stream);
241 
242   va_list ap;
243 
244   va_start (ap, format);
245   vfprintf (stream, format, ap);
246   va_end (ap);
247 }
248 
249 ////////////////////////////////////////////////////////////////////////////////
250 void
print_error(const char * format,...)251 pcl::console::print_error (const char *format, ...)
252 {
253   if (!isVerbosityLevelEnabled (L_ERROR)) return;
254 
255   change_text_color (stderr, TT_BRIGHT, TT_RED);
256   va_list ap;
257 
258   va_start (ap, format);
259   vfprintf (stderr, format, ap);
260   va_end (ap);
261 
262   reset_text_color (stderr);
263 }
264 
265 ////////////////////////////////////////////////////////////////////////////////
266 void
print_error(FILE * stream,const char * format,...)267 pcl::console::print_error (FILE *stream, const char *format, ...)
268 {
269   if (!isVerbosityLevelEnabled (L_ERROR)) return;
270 
271   change_text_color (stream, TT_BRIGHT, TT_RED);
272   va_list ap;
273 
274   va_start (ap, format);
275   vfprintf (stream, format, ap);
276   va_end (ap);
277 
278   reset_text_color (stream);
279 }
280 
281 ////////////////////////////////////////////////////////////////////////////////
282 void
print_warn(const char * format,...)283 pcl::console::print_warn (const char *format, ...)
284 {
285   if (!isVerbosityLevelEnabled (L_WARN)) return;
286 
287   change_text_color (stderr, TT_BRIGHT, TT_YELLOW);
288   va_list ap;
289 
290   va_start (ap, format);
291   vfprintf (stderr, format, ap);
292   va_end (ap);
293 
294   reset_text_color (stderr);
295 }
296 
297 ////////////////////////////////////////////////////////////////////////////////
298 void
print_warn(FILE * stream,const char * format,...)299 pcl::console::print_warn (FILE *stream, const char *format, ...)
300 {
301   if (!isVerbosityLevelEnabled (L_WARN)) return;
302 
303   change_text_color (stream, TT_BRIGHT, TT_YELLOW);
304   va_list ap;
305 
306   va_start (ap, format);
307   vfprintf (stream, format, ap);
308   va_end (ap);
309 
310   reset_text_color (stream);
311 }
312 
313 ////////////////////////////////////////////////////////////////////////////////
314 void
print_value(const char * format,...)315 pcl::console::print_value (const char *format, ...)
316 {
317   //if (!isVerbosityLevelEnabled (L_ALWAYS)) return;
318 
319   change_text_color (stdout, TT_RESET, TT_CYAN);
320   va_list ap;
321 
322   va_start (ap, format);
323   vfprintf (stdout, format, ap);
324   va_end (ap);
325 
326   reset_text_color (stdout);
327 }
328 
329 ////////////////////////////////////////////////////////////////////////////////
330 void
print_value(FILE * stream,const char * format,...)331 pcl::console::print_value (FILE *stream, const char *format, ...)
332 {
333   //if (!isVerbosityLevelEnabled (L_ALWAYS)) return;
334 
335   change_text_color (stream, TT_RESET, TT_CYAN);
336   va_list ap;
337 
338   va_start (ap, format);
339   vfprintf (stream, format, ap);
340   va_end (ap);
341 
342   reset_text_color (stream);
343 }
344 
345 ////////////////////////////////////////////////////////////////////////////////
346 void
print_debug(const char * format,...)347 pcl::console::print_debug (const char *format, ...)
348 {
349   if (!isVerbosityLevelEnabled (L_DEBUG)) return;
350 
351   change_text_color (stdout, TT_RESET, TT_GREEN);
352   va_list ap;
353 
354   va_start (ap, format);
355   vfprintf (stdout, format, ap);
356   va_end (ap);
357 
358   reset_text_color (stdout);
359 }
360 
361 ////////////////////////////////////////////////////////////////////////////////
362 void
print_debug(FILE * stream,const char * format,...)363 pcl::console::print_debug (FILE *stream, const char *format, ...)
364 {
365   if (!isVerbosityLevelEnabled (L_DEBUG)) return;
366 
367   change_text_color (stream, TT_RESET, TT_GREEN);
368   va_list ap;
369 
370   va_start (ap, format);
371   vfprintf (stream, format, ap);
372   va_end (ap);
373 
374   reset_text_color (stream);
375 }
376 
377 ////////////////////////////////////////////////////////////////////////////////
378 namespace pcl
379 {
380   namespace console
381   {
382     static bool s_NeedVerbosityInit = true;
383 #ifdef VERBOSITY_LEVEL_ALWAYS
384   static VERBOSITY_LEVEL s_VerbosityLevel = pcl::console::L_ALWAYS;
385 #elif defined VERBOSITY_LEVEL_ERROR
386   static VERBOSITY_LEVEL s_VerbosityLevel = pcl::console::L_ERROR;
387 #elif defined VERBOSITY_LEVEL_WARN
388   static VERBOSITY_LEVEL s_VerbosityLevel = pcl::console::L_WARN;
389 #elif defined VERBOSITY_LEVEL_DEBUG
390   static VERBOSITY_LEVEL s_VerbosityLevel = pcl::console::L_DEBUG;
391 #elif defined VERBOSITY_LEVEL_VERBOSE
392   static VERBOSITY_LEVEL s_VerbosityLevel = pcl::console::L_VERBOSE;
393 #else
394   static VERBOSITY_LEVEL s_VerbosityLevel = pcl::console::L_INFO;
395 #endif
396   }
397 }
398 
399 ////////////////////////////////////////////////////////////////////////////////
setVerbosityLevel(pcl::console::VERBOSITY_LEVEL level)400 void pcl::console::setVerbosityLevel (pcl::console::VERBOSITY_LEVEL level)
401 {
402   if (s_NeedVerbosityInit) pcl::console::initVerbosityLevel ();
403   s_VerbosityLevel = level;
404 }
405 
406 ////////////////////////////////////////////////////////////////////////////////
407 pcl::console::VERBOSITY_LEVEL
getVerbosityLevel()408 pcl::console::getVerbosityLevel ()
409 {
410   if (s_NeedVerbosityInit) pcl::console::initVerbosityLevel ();
411   return s_VerbosityLevel;
412 }
413 
414 ////////////////////////////////////////////////////////////////////////////////
415 bool
isVerbosityLevelEnabled(pcl::console::VERBOSITY_LEVEL level)416 pcl::console::isVerbosityLevelEnabled (pcl::console::VERBOSITY_LEVEL level)
417 {
418   if (s_NeedVerbosityInit) pcl::console::initVerbosityLevel ();
419   return level <= s_VerbosityLevel;
420 }
421 
422 ////////////////////////////////////////////////////////////////////////////////
423 bool
initVerbosityLevel()424 pcl::console::initVerbosityLevel ()
425 {
426 #ifdef VERBOSITY_LEVEL_ALWAYS
427   s_VerbosityLevel = pcl::console::L_ALWAYS;
428 #elif defined VERBOSITY_LEVEL_ERROR
429   s_VerbosityLevel = pcl::console::L_ERROR;
430 #elif defined VERBOSITY_LEVEL_WARN
431   s_VerbosityLevel = pcl::console::L_WARN;
432 #elif defined VERBOSITY_LEVEL_DEBUG
433   s_VerbosityLevel = pcl::console::L_DEBUG;
434 #elif defined VERBOSITY_LEVEL_VERBOSE
435   s_VerbosityLevel = pcl::console::L_VERBOSE;
436 #else
437   s_VerbosityLevel = pcl::console::L_INFO; // Default value
438 #endif
439 
440   char* pcl_verbosity_level = getenv ( "PCL_VERBOSITY_LEVEL");
441   if (pcl_verbosity_level)
442   {
443     std::string s_pcl_verbosity_level (pcl_verbosity_level);
444     std::transform (s_pcl_verbosity_level.begin (), s_pcl_verbosity_level.end (), s_pcl_verbosity_level.begin (), toupper);
445 
446     if (s_pcl_verbosity_level.find ("ALWAYS") != std::string::npos)          s_VerbosityLevel = L_ALWAYS;
447     else if (s_pcl_verbosity_level.find ("ERROR") != std::string::npos)      s_VerbosityLevel = L_ERROR;
448     else if (s_pcl_verbosity_level.find ("WARN") != std::string::npos)       s_VerbosityLevel = L_WARN;
449     else if (s_pcl_verbosity_level.find ("INFO") != std::string::npos)       s_VerbosityLevel = L_INFO;
450     else if (s_pcl_verbosity_level.find ("DEBUG") != std::string::npos)      s_VerbosityLevel = L_DEBUG;
451     else if (s_pcl_verbosity_level.find ("VERBOSE") != std::string::npos)    s_VerbosityLevel = L_VERBOSE;
452     else printf ("Warning: invalid PCL_VERBOSITY_LEVEL set (%s)\n", s_pcl_verbosity_level.c_str ());
453   }
454 
455   s_NeedVerbosityInit = false;
456   return true;
457 }
458 
459 ////////////////////////////////////////////////////////////////////////////////
460 void
print(pcl::console::VERBOSITY_LEVEL level,FILE * stream,const char * format,...)461 pcl::console::print (pcl::console::VERBOSITY_LEVEL level, FILE *stream, const char *format, ...)
462 {
463   if (!isVerbosityLevelEnabled (level)) return;
464   switch (level)
465   {
466     case L_DEBUG:
467       change_text_color (stream, TT_RESET, TT_GREEN);
468       break;
469     case L_WARN:
470       change_text_color (stream, TT_BRIGHT, TT_YELLOW);
471       break;
472     case L_ERROR:
473       change_text_color (stream, TT_BRIGHT, TT_RED);
474       break;
475     case L_ALWAYS:
476     case L_INFO:
477     case L_VERBOSE:
478     default:
479       break;
480   }
481 
482   va_list ap;
483 
484   va_start (ap, format);
485   vfprintf (stream, format, ap);
486   va_end (ap);
487 
488   reset_text_color (stream);
489 }
490 
491 ////////////////////////////////////////////////////////////////////////////////
492 void
print(pcl::console::VERBOSITY_LEVEL level,const char * format,...)493 pcl::console::print (pcl::console::VERBOSITY_LEVEL level, const char *format, ...)
494 {
495   if (!isVerbosityLevelEnabled (level)) return;
496   FILE *stream = (level == L_WARN || level == L_ERROR) ? stderr : stdout;
497   switch (level)
498   {
499     case L_DEBUG:
500       change_text_color (stream, TT_RESET, TT_GREEN);
501       break;
502     case L_WARN:
503       change_text_color (stream, TT_BRIGHT, TT_YELLOW);
504       break;
505     case L_ERROR:
506       change_text_color (stream, TT_BRIGHT, TT_RED);
507       break;
508     case L_ALWAYS:
509     case L_INFO:
510     case L_VERBOSE:
511     default:
512       break;
513   }
514 
515   va_list ap;
516 
517   va_start (ap, format);
518   vfprintf (stream, format, ap);
519   va_end (ap);
520 
521   reset_text_color (stream);
522 
523 }
524