blob: c5a89257c332c845919b3769b79a7d8687bc0f68 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2010 VMware, Inc.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
**************************************************************************/
/*
* Helpers for coloring output.
*/
#ifdef _WIN32
#define DEFINE_CONSOLEV2_PROPERTIES
#include <windows.h>
#include <io.h> // _isatty
#include <stdio.h> // _fileno
#ifndef COMMON_LVB_LEADING_BYTE
#define COMMON_LVB_LEADING_BYTE 0x0100
#endif
#ifndef COMMON_LVB_TRAILING_BYTE
#define COMMON_LVB_TRAILING_BYTE 0x0200
#endif
#ifndef COMMON_LVB_GRID_HORIZONTAL
#define COMMON_LVB_GRID_HORIZONTAL 0x0400
#endif
#ifndef COMMON_LVB_GRID_LVERTICAL
#define COMMON_LVB_GRID_LVERTICAL 0x0800
#endif
#ifndef COMMON_LVB_GRID_RVERTICAL
#define COMMON_LVB_GRID_RVERTICAL 0x1000
#endif
#ifndef COMMON_LVB_REVERSE_VIDEO
#define COMMON_LVB_REVERSE_VIDEO 0x4000
#endif
#ifndef COMMON_LVB_UNDERSCORE
#define COMMON_LVB_UNDERSCORE 0x8000
#endif
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
#else /* !_WIN32 */
#include <unistd.h> // isatty
#endif /* !_WIN32 */
#include "highlight.hpp"
namespace highlight {
class PlainAttribute : public Attribute {
public:
PlainAttribute(void) {}
virtual void apply(std::ostream &) const override {}
};
static const PlainAttribute plainAttribute;
class PlainHighlighter : public Highlighter {
public:
PlainHighlighter(void) {}
virtual const Attribute & normal(void) const override { return plainAttribute; }
virtual const Attribute & bold(void) const override { return plainAttribute; }
virtual const Attribute & italic(void) const override { return plainAttribute; }
virtual const Attribute & strike(void) const override { return plainAttribute; }
virtual const Attribute & color(Color) const override { return plainAttribute; }
};
static const PlainHighlighter plainHighlighter;
#define CSI "\33["
class AnsiAttribute : public Attribute {
protected:
const char *escape;
public:
AnsiAttribute(const char *_escape) :
escape(_escape)
{}
void apply(std::ostream& os) const override {
os << escape;
}
};
static const AnsiAttribute ansiNormal(CSI "0m");
static const AnsiAttribute ansiBold (CSI "1m");
static const AnsiAttribute ansiItalic(CSI "3m");
static const AnsiAttribute ansiStrike(CSI "9m");
#ifdef _WIN32
// Brighter colors on Windows to contrast with the background, and match the
// colors from SetConsoleTextAttribute
static const AnsiAttribute ansiRed (CSI "91m");
static const AnsiAttribute ansiGreen (CSI "92m");
static const AnsiAttribute ansiBlue (CSI "94m");
static const AnsiAttribute ansiGray (CSI "37m");
#else
static const AnsiAttribute ansiRed (CSI "31m");
static const AnsiAttribute ansiGreen (CSI "32m");
static const AnsiAttribute ansiBlue (CSI "34m");
static const AnsiAttribute ansiGray (CSI "37m");
#endif
/**
* Highlighter for plain-text files which outputs ANSI escape codes. See
* http://en.wikipedia.org/wiki/ANSI_escape_code for more information
* concerning ANSI escape codes.
*/
class AnsiHighlighter : public Highlighter {
public:
AnsiHighlighter(void) {}
virtual const Attribute & normal(void) const override { return ansiNormal; }
virtual const Attribute & bold(void) const override { return ansiBold; }
virtual const Attribute & italic(void) const override {
/* Italic is not widely supported, or worse, implemented with a reverse */
if (0)
return ansiItalic;
else
return plainAttribute;
}
virtual const Attribute & strike(void) const override { return ansiStrike; }
virtual const Attribute & color(Color c) const override {
switch (c) {
case RED:
return ansiRed;
case GREEN:
return ansiGreen;
case BLUE:
return ansiBlue;
case GRAY:
return ansiGray;
default:
return plainAttribute;
}
}
};
static const AnsiHighlighter ansiHighlighter;
#ifdef _WIN32
class WindowsAttribute : public Attribute {
protected:
WORD wAttributes;
public:
WindowsAttribute(WORD _wAttributes) :
wAttributes(_wAttributes)
{}
void apply(std::ostream& os) const {
DWORD nStdHandleOutput;
if (&os == &std::cout) {
nStdHandleOutput = STD_OUTPUT_HANDLE;
} else if (&os == &std::cerr) {
nStdHandleOutput = STD_ERROR_HANDLE;
} else {
return;
}
HANDLE hConsoleOutput = GetStdHandle(nStdHandleOutput);
if (hConsoleOutput == INVALID_HANDLE_VALUE) {
return;
}
SetConsoleTextAttribute(hConsoleOutput, wAttributes);
}
};
static const WindowsAttribute winNormal(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
static const WindowsAttribute winBold(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY);
static const WindowsAttribute winItalic(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
static const WindowsAttribute winStrike(COMMON_LVB_REVERSE_VIDEO | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
static const WindowsAttribute winRed(FOREGROUND_RED | FOREGROUND_INTENSITY);
static const WindowsAttribute winGreen(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
static const WindowsAttribute winBlue(FOREGROUND_BLUE | FOREGROUND_INTENSITY);
/**
* Highlighter for the Windows Console.
*/
class WindowsHighlighter : public Highlighter {
public:
virtual const Attribute & normal(void) const { return winNormal; }
virtual const Attribute & bold(void) const { return winBold; }
virtual const Attribute & italic(void) const { return winItalic; }
virtual const Attribute & strike(void) const { return winStrike; }
virtual const Attribute & color(Color c) const {
switch (c) {
case RED:
return winRed;
case GREEN:
return winGreen;
case BLUE:
return winBlue;
default:
return plainAttribute;
}
}
};
static const WindowsHighlighter windowsHighlighter;
static bool
haveAnsi(void)
{
static bool checked = false;
static bool result = false;
if (!checked) {
checked = true;
// http://wiki.winehq.org/DeveloperFaq#detect-wine
HMODULE hNtDll = GetModuleHandleA("ntdll");
if (hNtDll &&
GetProcAddress(hNtDll, "wine_get_version") != nullptr) {
return result = true;
}
// Cygwin shell
if (1) {
const char *term = getenv("TERM");
if (term &&
strcmp(term, "xterm") == 0) {
return result = true;
}
}
// Set output mode to handle virtual terminal sequences
// https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032.aspx
// TODO: Use CONOUT$
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075.aspx
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE) {
return false;
}
DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode)) {
return false;
}
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (SetConsoleMode(hOut, dwMode)) {
return result = true;
} else {
return false;
}
}
return result;
}
#endif /* _WIN32 */
const Highlighter &
defaultHighlighter(bool color) {
if (color) {
#ifdef _WIN32
if (haveAnsi()) {
return ansiHighlighter;
} else {
return windowsHighlighter;
}
#else
return ansiHighlighter;
#endif
} else {
return plainHighlighter;
}
}
bool
isAtty(std::ostream &os) {
int fd;
if (&os == &std::cout) {
#ifdef _WIN32
fd = _fileno(stdout);
#else
fd = STDOUT_FILENO;
#endif
} else if (&os == &std::cerr) {
#ifdef _WIN32
fd = _fileno(stderr);
#else
fd = STDERR_FILENO;
#endif
} else {
return false;
}
#ifdef _WIN32
return _isatty(fd);
#else
return isatty(fd);
#endif
}
const Highlighter &
defaultHighlighter(std::ostream & os)
{
return defaultHighlighter(isAtty(os));
}
} /* namespace highlight */