/* 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) # define KWSYS_TERMINAL_SUPPORT_CONSOLE #endif #if !defined(_WIN32) # define KWSYS_TERMINAL_ISATTY_WORKS #endif /* Include needed system APIs. */ #include /* va_list */ #include /* getenv */ #include /* strcmp */ #if defined(KWSYS_TERMINAL_SUPPORT_CONSOLE) # include /* _get_osfhandle */ # include /* SetConsoleTextAttribute */ #endif #if defined(KWSYS_TERMINAL_ISATTY_WORKS) # include /* isatty */ #else # include /* 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", "alacritty", "alacritty-direct", "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", "st-256color", "tmux", "tmux-256color", "vt100", "xterm", "xterm-16color", "xterm-256color", "xterm-88color", "xterm-color", "xterm-debian", "xterm-kitty", "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; } } /* Disable color according to http://bixense.com/clicolors/ convention. */ { const char* clicolor = getenv("CLICOLOR"); if (clicolor && strcmp(clicolor, "0") == 0) { return 0; } } /* GNU make 4.1+ may tell us that its output is destined for a TTY. */ { const char* termout = getenv("MAKE_TERMOUT"); if (termout && *termout != '\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. */ #if defined(__MVS__) /* if building on z/OS (aka MVS), assume we are using EBCDIC */ # define ESCAPE_CHAR "\47" #else # define ESCAPE_CHAR "\33" #endif #define KWSYS_TERMINAL_VT100_NORMAL ESCAPE_CHAR "[0m" #define KWSYS_TERMINAL_VT100_BOLD ESCAPE_CHAR "[1m" #define KWSYS_TERMINAL_VT100_UNDERLINE ESCAPE_CHAR "[4m" #define KWSYS_TERMINAL_VT100_BLINK ESCAPE_CHAR "[5m" #define KWSYS_TERMINAL_VT100_INVERSE ESCAPE_CHAR "[7m" #define KWSYS_TERMINAL_VT100_FOREGROUND_BLACK ESCAPE_CHAR "[30m" #define KWSYS_TERMINAL_VT100_FOREGROUND_RED ESCAPE_CHAR "[31m" #define KWSYS_TERMINAL_VT100_FOREGROUND_GREEN ESCAPE_CHAR "[32m" #define KWSYS_TERMINAL_VT100_FOREGROUND_YELLOW ESCAPE_CHAR "[33m" #define KWSYS_TERMINAL_VT100_FOREGROUND_BLUE ESCAPE_CHAR "[34m" #define KWSYS_TERMINAL_VT100_FOREGROUND_MAGENTA ESCAPE_CHAR "[35m" #define KWSYS_TERMINAL_VT100_FOREGROUND_CYAN ESCAPE_CHAR "[36m" #define KWSYS_TERMINAL_VT100_FOREGROUND_WHITE ESCAPE_CHAR "[37m" #define KWSYS_TERMINAL_VT100_BACKGROUND_BLACK ESCAPE_CHAR "[40m" #define KWSYS_TERMINAL_VT100_BACKGROUND_RED ESCAPE_CHAR "[41m" #define KWSYS_TERMINAL_VT100_BACKGROUND_GREEN ESCAPE_CHAR "[42m" #define KWSYS_TERMINAL_VT100_BACKGROUND_YELLOW ESCAPE_CHAR "[43m" #define KWSYS_TERMINAL_VT100_BACKGROUND_BLUE ESCAPE_CHAR "[44m" #define KWSYS_TERMINAL_VT100_BACKGROUND_MAGENTA ESCAPE_CHAR "[45m" #define KWSYS_TERMINAL_VT100_BACKGROUND_CYAN ESCAPE_CHAR "[46m" #define KWSYS_TERMINAL_VT100_BACKGROUND_WHITE ESCAPE_CHAR "[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