Terminal.c
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
#include "kwsysPrivate.h"
#include KWSYS_HEADER(Terminal.h)
/* Work-around CMake dependency scanning limitation. This must
duplicate the above list of headers. */
#if 0
#include "Terminal.h.in"
#endif
/* Configure support for this platform. */
#if defined(_WIN32) || defined(__CYGWIN__)
#define KWSYS_TERMINAL_SUPPORT_CONSOLE
#endif
#if !defined(_WIN32)
#define KWSYS_TERMINAL_ISATTY_WORKS
#endif
/* Include needed system APIs. */
#include <stdarg.h> /* va_list */
#include <stdlib.h> /* getenv */
#include <string.h> /* strcmp */
#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
#include <io.h> /* _get_osfhandle */
#include <windows.h> /* SetConsoleTextAttribute */
#endif
#if defined(KWSYS_TERMINAL_ISATTY_WORKS)
#include <unistd.h> /* isatty */
#else
#include <sys/stat.h> /* fstat */
#endif
static int kwsysTerminalStreamIsVT100(FILE* stream, int default_vt100,
int default_tty);
static void kwsysTerminalSetVT100Color(FILE* stream, int color);
#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
static HANDLE kwsysTerminalGetStreamHandle(FILE* stream);
static void kwsysTerminalSetConsoleColor(HANDLE hOut,
CONSOLE_SCREEN_BUFFER_INFO* hOutInfo,
FILE* stream, int color);
#endif
void kwsysTerminal_cfprintf(int color, FILE* stream, const char* format, ...)
{
/* Setup the stream with the given color if possible. */
int pipeIsConsole = 0;
int pipeIsVT100 = 0;
int default_vt100 = color & kwsysTerminal_Color_AssumeVT100;
int default_tty = color & kwsysTerminal_Color_AssumeTTY;
#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
CONSOLE_SCREEN_BUFFER_INFO hOutInfo;
HANDLE hOut = kwsysTerminalGetStreamHandle(stream);
if (GetConsoleScreenBufferInfo(hOut, &hOutInfo)) {
pipeIsConsole = 1;
kwsysTerminalSetConsoleColor(hOut, &hOutInfo, stream, color);
}
#endif
if (!pipeIsConsole &&
kwsysTerminalStreamIsVT100(stream, default_vt100, default_tty)) {
pipeIsVT100 = 1;
kwsysTerminalSetVT100Color(stream, color);
}
/* Format the text into the stream. */
{
va_list var_args;
va_start(var_args, format);
vfprintf(stream, format, var_args);
va_end(var_args);
}
/* Restore the normal color state for the stream. */
#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
if (pipeIsConsole) {
kwsysTerminalSetConsoleColor(hOut, &hOutInfo, stream,
kwsysTerminal_Color_Normal);
}
#endif
if (pipeIsVT100) {
kwsysTerminalSetVT100Color(stream, kwsysTerminal_Color_Normal);
}
}
/* Detect cases when a stream is definitely not interactive. */
#if !defined(KWSYS_TERMINAL_ISATTY_WORKS)
static int kwsysTerminalStreamIsNotInteractive(FILE* stream)
{
/* The given stream is definitely not interactive if it is a regular
file. */
struct stat stream_stat;
if (fstat(fileno(stream), &stream_stat) == 0) {
if (stream_stat.st_mode & S_IFREG) {
return 1;
}
}
return 0;
}
#endif
/* List of terminal names known to support VT100 color escape sequences. */
static const char* kwsysTerminalVT100Names[] = { "Eterm",
"ansi",
"color-xterm",
"con132x25",
"con132x30",
"con132x43",
"con132x60",
"con80x25",
"con80x28",
"con80x30",
"con80x43",
"con80x50",
"con80x60",
"cons25",
"console",
"cygwin",
"dtterm",
"eterm-color",
"gnome",
"gnome-256color",
"konsole",
"konsole-256color",
"kterm",
"linux",
"msys",
"linux-c",
"mach-color",
"mlterm",
"putty",
"putty-256color",
"rxvt",
"rxvt-256color",
"rxvt-cygwin",
"rxvt-cygwin-native",
"rxvt-unicode",
"rxvt-unicode-256color",
"screen",
"screen-256color",
"screen-256color-bce",
"screen-bce",
"screen-w",
"screen.linux",
"tmux",
"tmux-256color",
"vt100",
"xterm",
"xterm-16color",
"xterm-256color",
"xterm-88color",
"xterm-color",
"xterm-debian",
"xterm-termite",
0 };
/* Detect whether a stream is displayed in a VT100-compatible terminal. */
static int kwsysTerminalStreamIsVT100(FILE* stream, int default_vt100,
int default_tty)
{
/* Force color according to http://bixense.com/clicolors/ convention. */
{
const char* clicolor_force = getenv("CLICOLOR_FORCE");
if (clicolor_force && *clicolor_force &&
strcmp(clicolor_force, "0") != 0) {
return 1;
}
}
/* If running inside emacs the terminal is not VT100. Some emacs
seem to claim the TERM is xterm even though they do not support
VT100 escapes. */
{
const char* emacs = getenv("EMACS");
if (emacs && *emacs == 't') {
return 0;
}
}
/* Check for a valid terminal. */
if (!default_vt100) {
const char** t = 0;
const char* term = getenv("TERM");
if (term) {
for (t = kwsysTerminalVT100Names; *t && strcmp(term, *t) != 0; ++t) {
}
}
if (!(t && *t)) {
return 0;
}
}
#if defined(KWSYS_TERMINAL_ISATTY_WORKS)
/* Make sure the stream is a tty. */
(void)default_tty;
return isatty(fileno(stream)) ? 1 : 0;
#else
/* Check for cases in which the stream is definitely not a tty. */
if (kwsysTerminalStreamIsNotInteractive(stream)) {
return 0;
}
/* Use the provided default for whether this is a tty. */
return default_tty;
#endif
}
/* VT100 escape sequence strings. */
#define KWSYS_TERMINAL_VT100_NORMAL "\33[0m"
#define KWSYS_TERMINAL_VT100_BOLD "\33[1m"
#define KWSYS_TERMINAL_VT100_UNDERLINE "\33[4m"
#define KWSYS_TERMINAL_VT100_BLINK "\33[5m"
#define KWSYS_TERMINAL_VT100_INVERSE "\33[7m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_BLACK "\33[30m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_RED "\33[31m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_GREEN "\33[32m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_YELLOW "\33[33m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_BLUE "\33[34m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_MAGENTA "\33[35m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_CYAN "\33[36m"
#define KWSYS_TERMINAL_VT100_FOREGROUND_WHITE "\33[37m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_BLACK "\33[40m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_RED "\33[41m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_GREEN "\33[42m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_YELLOW "\33[43m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_BLUE "\33[44m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_MAGENTA "\33[45m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_CYAN "\33[46m"
#define KWSYS_TERMINAL_VT100_BACKGROUND_WHITE "\33[47m"
/* Write VT100 escape sequences to the stream for the given color. */
static void kwsysTerminalSetVT100Color(FILE* stream, int color)
{
if (color == kwsysTerminal_Color_Normal) {
fprintf(stream, KWSYS_TERMINAL_VT100_NORMAL);
return;
}
switch (color & kwsysTerminal_Color_ForegroundMask) {
case kwsysTerminal_Color_Normal:
fprintf(stream, KWSYS_TERMINAL_VT100_NORMAL);
break;
case kwsysTerminal_Color_ForegroundBlack:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_BLACK);
break;
case kwsysTerminal_Color_ForegroundRed:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_RED);
break;
case kwsysTerminal_Color_ForegroundGreen:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_GREEN);
break;
case kwsysTerminal_Color_ForegroundYellow:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_YELLOW);
break;
case kwsysTerminal_Color_ForegroundBlue:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_BLUE);
break;
case kwsysTerminal_Color_ForegroundMagenta:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_MAGENTA);
break;
case kwsysTerminal_Color_ForegroundCyan:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_CYAN);
break;
case kwsysTerminal_Color_ForegroundWhite:
fprintf(stream, KWSYS_TERMINAL_VT100_FOREGROUND_WHITE);
break;
}
switch (color & kwsysTerminal_Color_BackgroundMask) {
case kwsysTerminal_Color_BackgroundBlack:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_BLACK);
break;
case kwsysTerminal_Color_BackgroundRed:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_RED);
break;
case kwsysTerminal_Color_BackgroundGreen:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_GREEN);
break;
case kwsysTerminal_Color_BackgroundYellow:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_YELLOW);
break;
case kwsysTerminal_Color_BackgroundBlue:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_BLUE);
break;
case kwsysTerminal_Color_BackgroundMagenta:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_MAGENTA);
break;
case kwsysTerminal_Color_BackgroundCyan:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_CYAN);
break;
case kwsysTerminal_Color_BackgroundWhite:
fprintf(stream, KWSYS_TERMINAL_VT100_BACKGROUND_WHITE);
break;
}
if (color & kwsysTerminal_Color_ForegroundBold) {
fprintf(stream, KWSYS_TERMINAL_VT100_BOLD);
}
}
#if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE)
#define KWSYS_TERMINAL_MASK_FOREGROUND \
(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY)
#define KWSYS_TERMINAL_MASK_BACKGROUND \
(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY)
/* Get the Windows handle for a FILE stream. */
static HANDLE kwsysTerminalGetStreamHandle(FILE* stream)
{
/* Get the C-library file descriptor from the stream. */
int fd = fileno(stream);
#if defined(__CYGWIN__)
/* Cygwin seems to have an extra pipe level. If the file descriptor
corresponds to stdout or stderr then obtain the matching windows
handle directly. */
if (fd == fileno(stdout)) {
return GetStdHandle(STD_OUTPUT_HANDLE);
} else if (fd == fileno(stderr)) {
return GetStdHandle(STD_ERROR_HANDLE);
}
#endif
/* Get the underlying Windows handle for the descriptor. */
return (HANDLE)_get_osfhandle(fd);
}
/* Set color attributes in a Windows console. */
static void kwsysTerminalSetConsoleColor(HANDLE hOut,
CONSOLE_SCREEN_BUFFER_INFO* hOutInfo,
FILE* stream, int color)
{
WORD attributes = 0;
switch (color & kwsysTerminal_Color_ForegroundMask) {
case kwsysTerminal_Color_Normal:
attributes |= hOutInfo->wAttributes & KWSYS_TERMINAL_MASK_FOREGROUND;
break;
case kwsysTerminal_Color_ForegroundBlack:
attributes |= 0;
break;
case kwsysTerminal_Color_ForegroundRed:
attributes |= FOREGROUND_RED;
break;
case kwsysTerminal_Color_ForegroundGreen:
attributes |= FOREGROUND_GREEN;
break;
case kwsysTerminal_Color_ForegroundYellow:
attributes |= FOREGROUND_RED | FOREGROUND_GREEN;
break;
case kwsysTerminal_Color_ForegroundBlue:
attributes |= FOREGROUND_BLUE;
break;
case kwsysTerminal_Color_ForegroundMagenta:
attributes |= FOREGROUND_RED | FOREGROUND_BLUE;
break;
case kwsysTerminal_Color_ForegroundCyan:
attributes |= FOREGROUND_BLUE | FOREGROUND_GREEN;
break;
case kwsysTerminal_Color_ForegroundWhite:
attributes |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
break;
}
switch (color & kwsysTerminal_Color_BackgroundMask) {
case kwsysTerminal_Color_Normal:
attributes |= hOutInfo->wAttributes & KWSYS_TERMINAL_MASK_BACKGROUND;
break;
case kwsysTerminal_Color_BackgroundBlack:
attributes |= 0;
break;
case kwsysTerminal_Color_BackgroundRed:
attributes |= BACKGROUND_RED;
break;
case kwsysTerminal_Color_BackgroundGreen:
attributes |= BACKGROUND_GREEN;
break;
case kwsysTerminal_Color_BackgroundYellow:
attributes |= BACKGROUND_RED | BACKGROUND_GREEN;
break;
case kwsysTerminal_Color_BackgroundBlue:
attributes |= BACKGROUND_BLUE;
break;
case kwsysTerminal_Color_BackgroundMagenta:
attributes |= BACKGROUND_RED | BACKGROUND_BLUE;
break;
case kwsysTerminal_Color_BackgroundCyan:
attributes |= BACKGROUND_BLUE | BACKGROUND_GREEN;
break;
case kwsysTerminal_Color_BackgroundWhite:
attributes |= BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
break;
}
if (color & kwsysTerminal_Color_ForegroundBold) {
attributes |= FOREGROUND_INTENSITY;
}
if (color & kwsysTerminal_Color_BackgroundBold) {
attributes |= BACKGROUND_INTENSITY;
}
fflush(stream);
SetConsoleTextAttribute(hOut, attributes);
}
#endif