1 /*=========================================================================
3 Program: KWSys - Kitware System Library
4 Module: $RCSfile: Terminal.c,v $
6 Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved.
7 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the above copyright notices for more information.
13 =========================================================================*/
14 #include "kwsysPrivate.h"
15 #include KWSYS_HEADER(Terminal.h)
17 /* Work-around CMake dependency scanning limitation. This must
18 duplicate the above list of headers. */
20 # include "Terminal.h.in"
23 /*--------------------------------------------------------------------------*/
24 /* Configure support for this platform. */
25 #if defined(_WIN32) || defined(__CYGWIN__)
26 # define KWSYS_TERMINAL_SUPPORT_CONSOLE
29 # define KWSYS_TERMINAL_ISATTY_WORKS
32 /*--------------------------------------------------------------------------*/
33 /* Include needed system APIs. */
35 #include <stdlib.h> /* getenv */
36 #include <string.h> /* strcmp */
37 #include <stdarg.h> /* va_list */
39 #if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
40 # include <windows.h> /* SetConsoleTextAttribute */
41 # include <io.h> /* _get_osfhandle */
44 #if defined(KWSYS_TERMINAL_ISATTY_WORKS)
45 # include <unistd.h> /* isatty */
47 # include <sys/stat.h> /* fstat */
50 /*--------------------------------------------------------------------------*/
51 static int kwsysTerminalStreamIsVT100(FILE* stream
, int default_vt100
,
53 static void kwsysTerminalSetVT100Color(FILE* stream
, int color
);
54 #if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
55 static HANDLE
kwsysTerminalGetStreamHandle(FILE* stream
);
56 static void kwsysTerminalSetConsoleColor(HANDLE hOut
,
57 CONSOLE_SCREEN_BUFFER_INFO
* hOutInfo
,
62 /*--------------------------------------------------------------------------*/
63 void kwsysTerminal_cfprintf(int color
, FILE* stream
, const char* format
, ...)
65 /* Setup the stream with the given color if possible. */
66 int pipeIsConsole
= 0;
68 int default_vt100
= color
& kwsysTerminal_Color_AssumeVT100
;
69 int default_tty
= color
& kwsysTerminal_Color_AssumeTTY
;
70 #if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
71 CONSOLE_SCREEN_BUFFER_INFO hOutInfo
;
72 HANDLE hOut
= kwsysTerminalGetStreamHandle(stream
);
73 if(GetConsoleScreenBufferInfo(hOut
, &hOutInfo
))
76 kwsysTerminalSetConsoleColor(hOut
, &hOutInfo
, stream
, color
);
79 if(!pipeIsConsole
&& kwsysTerminalStreamIsVT100(stream
,
80 default_vt100
, default_tty
))
83 kwsysTerminalSetVT100Color(stream
, color
);
86 /* Format the text into the stream. */
89 va_start(var_args
, format
);
90 vfprintf(stream
, format
, var_args
);
94 /* Restore the normal color state for the stream. */
95 #if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
98 kwsysTerminalSetConsoleColor(hOut
, &hOutInfo
, stream
,
99 kwsysTerminal_Color_Normal
);
104 kwsysTerminalSetVT100Color(stream
, kwsysTerminal_Color_Normal
);
108 /*--------------------------------------------------------------------------*/
109 /* Detect cases when a stream is definately not interactive. */
110 #if !defined(KWSYS_TERMINAL_ISATTY_WORKS)
111 static int kwsysTerminalStreamIsNotInteractive(FILE* stream
)
113 /* The given stream is definately not interactive if it is a regular
115 struct stat stream_stat
;
116 if(fstat(fileno(stream
), &stream_stat
) == 0)
118 if(stream_stat
.st_mode
& S_IFREG
)
127 /*--------------------------------------------------------------------------*/
128 /* List of terminal names known to support VT100 color escape sequences. */
129 static const char* kwsysTerminalVT100Names
[] =
161 /*--------------------------------------------------------------------------*/
162 /* Detect whether a stream is displayed in a VT100-compatible terminal. */
163 static int kwsysTerminalStreamIsVT100(FILE* stream
, int default_vt100
,
166 /* If running inside emacs the terminal is not VT100. Some emacs
167 seem to claim the TERM is xterm even though they do not support
169 const char* emacs
= getenv("EMACS");
170 if(emacs
&& *emacs
== 't')
175 /* Check for a valid terminal. */
179 const char* term
= getenv("TERM");
182 for(t
= kwsysTerminalVT100Names
; *t
&& strcmp(term
, *t
) != 0; ++t
) {}
190 #if defined(KWSYS_TERMINAL_ISATTY_WORKS)
191 /* Make sure the stream is a tty. */
193 return isatty(fileno(stream
))? 1:0;
195 /* Check for cases in which the stream is definately not a tty. */
196 if(kwsysTerminalStreamIsNotInteractive(stream
))
201 /* Use the provided default for whether this is a tty. */
206 /*--------------------------------------------------------------------------*/
207 /* VT100 escape sequence strings. */
208 #define KWSYS_TERMINAL_VT100_NORMAL "\33[0m"
209 #define KWSYS_TERMINAL_VT100_BOLD "\33[1m"
210 #define KWSYS_TERMINAL_VT100_UNDERLINE "\33[4m"
211 #define KWSYS_TERMINAL_VT100_BLINK "\33[5m"
212 #define KWSYS_TERMINAL_VT100_INVERSE "\33[7m"
213 #define KWSYS_TERMINAL_VT100_FOREGROUND_BLACK "\33[30m"
214 #define KWSYS_TERMINAL_VT100_FOREGROUND_RED "\33[31m"
215 #define KWSYS_TERMINAL_VT100_FOREGROUND_GREEN "\33[32m"
216 #define KWSYS_TERMINAL_VT100_FOREGROUND_YELLOW "\33[33m"
217 #define KWSYS_TERMINAL_VT100_FOREGROUND_BLUE "\33[34m"
218 #define KWSYS_TERMINAL_VT100_FOREGROUND_MAGENTA "\33[35m"
219 #define KWSYS_TERMINAL_VT100_FOREGROUND_CYAN "\33[36m"
220 #define KWSYS_TERMINAL_VT100_FOREGROUND_WHITE "\33[37m"
221 #define KWSYS_TERMINAL_VT100_BACKGROUND_BLACK "\33[40m"
222 #define KWSYS_TERMINAL_VT100_BACKGROUND_RED "\33[41m"
223 #define KWSYS_TERMINAL_VT100_BACKGROUND_GREEN "\33[42m"
224 #define KWSYS_TERMINAL_VT100_BACKGROUND_YELLOW "\33[43m"
225 #define KWSYS_TERMINAL_VT100_BACKGROUND_BLUE "\33[44m"
226 #define KWSYS_TERMINAL_VT100_BACKGROUND_MAGENTA "\33[45m"
227 #define KWSYS_TERMINAL_VT100_BACKGROUND_CYAN "\33[46m"
228 #define KWSYS_TERMINAL_VT100_BACKGROUND_WHITE "\33[47m"
230 /*--------------------------------------------------------------------------*/
231 /* Write VT100 escape sequences to the stream for the given color. */
232 static void kwsysTerminalSetVT100Color(FILE* stream
, int color
)
234 if(color
== kwsysTerminal_Color_Normal
)
236 fprintf(stream
, KWSYS_TERMINAL_VT100_NORMAL
);
240 switch(color
& kwsysTerminal_Color_ForegroundMask
)
242 case kwsysTerminal_Color_Normal
:
243 fprintf(stream
, KWSYS_TERMINAL_VT100_NORMAL
);
245 case kwsysTerminal_Color_ForegroundBlack
:
246 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_BLACK
);
248 case kwsysTerminal_Color_ForegroundRed
:
249 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_RED
);
251 case kwsysTerminal_Color_ForegroundGreen
:
252 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_GREEN
);
254 case kwsysTerminal_Color_ForegroundYellow
:
255 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_YELLOW
);
257 case kwsysTerminal_Color_ForegroundBlue
:
258 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_BLUE
);
260 case kwsysTerminal_Color_ForegroundMagenta
:
261 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_MAGENTA
);
263 case kwsysTerminal_Color_ForegroundCyan
:
264 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_CYAN
);
266 case kwsysTerminal_Color_ForegroundWhite
:
267 fprintf(stream
, KWSYS_TERMINAL_VT100_FOREGROUND_WHITE
);
270 switch(color
& kwsysTerminal_Color_BackgroundMask
)
272 case kwsysTerminal_Color_BackgroundBlack
:
273 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_BLACK
);
275 case kwsysTerminal_Color_BackgroundRed
:
276 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_RED
);
278 case kwsysTerminal_Color_BackgroundGreen
:
279 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_GREEN
);
281 case kwsysTerminal_Color_BackgroundYellow
:
282 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_YELLOW
);
284 case kwsysTerminal_Color_BackgroundBlue
:
285 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_BLUE
);
287 case kwsysTerminal_Color_BackgroundMagenta
:
288 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_MAGENTA
);
290 case kwsysTerminal_Color_BackgroundCyan
:
291 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_CYAN
);
293 case kwsysTerminal_Color_BackgroundWhite
:
294 fprintf(stream
, KWSYS_TERMINAL_VT100_BACKGROUND_WHITE
);
297 if(color
& kwsysTerminal_Color_ForegroundBold
)
299 fprintf(stream
, KWSYS_TERMINAL_VT100_BOLD
);
303 /*--------------------------------------------------------------------------*/
304 #if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
306 # define KWSYS_TERMINAL_MASK_FOREGROUND \
307 (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
308 # define KWSYS_TERMINAL_MASK_BACKGROUND \
309 (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
311 /* Get the Windows handle for a FILE stream. */
312 static HANDLE
kwsysTerminalGetStreamHandle(FILE* stream
)
314 /* Get the C-library file descriptor from the stream. */
315 int fd
= fileno(stream
);
317 # if defined(__CYGWIN__)
318 /* Cygwin seems to have an extra pipe level. If the file descriptor
319 corresponds to stdout or stderr then obtain the matching windows
321 if(fd
== fileno(stdout
))
323 return GetStdHandle(STD_OUTPUT_HANDLE
);
325 else if(fd
== fileno(stderr
))
327 return GetStdHandle(STD_ERROR_HANDLE
);
331 /* Get the underlying Windows handle for the descriptor. */
332 return (HANDLE
)_get_osfhandle(fd
);
335 /* Set color attributes in a Windows console. */
336 static void kwsysTerminalSetConsoleColor(HANDLE hOut
,
337 CONSOLE_SCREEN_BUFFER_INFO
* hOutInfo
,
342 switch(color
& kwsysTerminal_Color_ForegroundMask
)
344 case kwsysTerminal_Color_Normal
:
345 attributes
|= hOutInfo
->wAttributes
& KWSYS_TERMINAL_MASK_FOREGROUND
;
347 case kwsysTerminal_Color_ForegroundBlack
:
350 case kwsysTerminal_Color_ForegroundRed
:
351 attributes
|= FOREGROUND_RED
;
353 case kwsysTerminal_Color_ForegroundGreen
:
354 attributes
|= FOREGROUND_GREEN
;
356 case kwsysTerminal_Color_ForegroundYellow
:
357 attributes
|= FOREGROUND_RED
| FOREGROUND_GREEN
;
359 case kwsysTerminal_Color_ForegroundBlue
:
360 attributes
|= FOREGROUND_BLUE
;
362 case kwsysTerminal_Color_ForegroundMagenta
:
363 attributes
|= FOREGROUND_RED
| FOREGROUND_BLUE
;
365 case kwsysTerminal_Color_ForegroundCyan
:
366 attributes
|= FOREGROUND_BLUE
| FOREGROUND_GREEN
;
368 case kwsysTerminal_Color_ForegroundWhite
:
369 attributes
|= FOREGROUND_BLUE
| FOREGROUND_GREEN
| FOREGROUND_RED
;
372 switch(color
& kwsysTerminal_Color_BackgroundMask
)
374 case kwsysTerminal_Color_Normal
:
375 attributes
|= hOutInfo
->wAttributes
& KWSYS_TERMINAL_MASK_BACKGROUND
;
377 case kwsysTerminal_Color_BackgroundBlack
:
380 case kwsysTerminal_Color_BackgroundRed
:
381 attributes
|= BACKGROUND_RED
;
383 case kwsysTerminal_Color_BackgroundGreen
:
384 attributes
|= BACKGROUND_GREEN
;
386 case kwsysTerminal_Color_BackgroundYellow
:
387 attributes
|= BACKGROUND_RED
| BACKGROUND_GREEN
;
389 case kwsysTerminal_Color_BackgroundBlue
:
390 attributes
|= BACKGROUND_BLUE
;
392 case kwsysTerminal_Color_BackgroundMagenta
:
393 attributes
|= BACKGROUND_RED
| BACKGROUND_BLUE
;
395 case kwsysTerminal_Color_BackgroundCyan
:
396 attributes
|= BACKGROUND_BLUE
| BACKGROUND_GREEN
;
398 case kwsysTerminal_Color_BackgroundWhite
:
399 attributes
|= BACKGROUND_BLUE
| BACKGROUND_GREEN
| BACKGROUND_RED
;
402 if(color
& kwsysTerminal_Color_ForegroundBold
)
404 attributes
|= FOREGROUND_INTENSITY
;
406 if(color
& kwsysTerminal_Color_BackgroundBold
)
408 attributes
|= BACKGROUND_INTENSITY
;
411 SetConsoleTextAttribute(hOut
, attributes
);