mirror of
https://github.com/EQEmu/Server.git
synced 2026-04-05 23:42:31 +00:00
- License was intended to be GPLv3 per earlier commit of GPLv3 LICENSE FILE - This is confirmed by the inclusion of libraries that are incompatible with GPLv2 - This is also confirmed by KLS and the agreement of KLS's predecessors - Added GPLv3 license headers to the compilable source files - Removed Folly licensing in strings.h since the string functions do not match the Folly functions and are standard functions - this must have been left over from previous implementations - Removed individual contributor license headers since the project has been under the "developer" mantle for many years - Removed comments on files that were previously automatically generated since they've been manually modified multiple times and there are no automatic scripts referencing them (removed in 2023)
528 lines
15 KiB
C++
528 lines
15 KiB
C++
/* EQEmu: EQEmulator
|
|
|
|
Copyright (C) 2001-2026 EQEmu Development Team
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef RANG_DOT_HPP
|
|
#define RANG_DOT_HPP
|
|
|
|
#if defined(__unix__) || defined(__unix) || defined(__linux__)
|
|
#define OS_LINUX
|
|
#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64)
|
|
#define OS_WIN
|
|
#elif defined(__APPLE__) || defined(__MACH__)
|
|
#define OS_MAC
|
|
#else
|
|
#error Unknown Platform
|
|
#endif
|
|
|
|
#if defined(OS_LINUX) || defined(OS_MAC)
|
|
#include <unistd.h>
|
|
|
|
#elif defined(OS_WIN)
|
|
|
|
#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
|
|
#error \
|
|
"Please include rang.hpp before any windows system headers or set _WIN32_WINNT at least to _WIN32_WINNT_VISTA"
|
|
#elif !defined(_WIN32_WINNT)
|
|
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include <io.h>
|
|
#include <memory>
|
|
|
|
// Only defined in windows 10 onwards, redefining in lower windows since it
|
|
// doesn't gets used in lower versions
|
|
// https://docs.microsoft.com/en-us/windows/console/getconsolemode
|
|
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
inline bool g_is_forced_tty = std::getenv("IS_TTY");
|
|
|
|
namespace rang {
|
|
|
|
/* For better compability with most of terminals do not use any style settings
|
|
* except of reset, bold and reversed.
|
|
* Note that on Windows terminals bold style is same as fgB color.
|
|
*/
|
|
enum class style {
|
|
reset = 0,
|
|
bold = 1,
|
|
dim = 2,
|
|
italic = 3,
|
|
underline = 4,
|
|
blink = 5,
|
|
rblink = 6,
|
|
reversed = 7,
|
|
conceal = 8,
|
|
crossed = 9
|
|
};
|
|
|
|
enum class fg {
|
|
black = 30,
|
|
red = 31,
|
|
green = 32,
|
|
yellow = 33,
|
|
blue = 34,
|
|
magenta = 35,
|
|
cyan = 36,
|
|
gray = 37,
|
|
reset = 39
|
|
};
|
|
|
|
enum class bg {
|
|
black = 40,
|
|
red = 41,
|
|
green = 42,
|
|
yellow = 43,
|
|
blue = 44,
|
|
magenta = 45,
|
|
cyan = 46,
|
|
gray = 47,
|
|
reset = 49
|
|
};
|
|
|
|
enum class fgB {
|
|
black = 90,
|
|
red = 91,
|
|
green = 92,
|
|
yellow = 93,
|
|
blue = 94,
|
|
magenta = 95,
|
|
cyan = 96,
|
|
gray = 97
|
|
};
|
|
|
|
enum class bgB {
|
|
black = 100,
|
|
red = 101,
|
|
green = 102,
|
|
yellow = 103,
|
|
blue = 104,
|
|
magenta = 105,
|
|
cyan = 106,
|
|
gray = 107
|
|
};
|
|
|
|
enum class control { // Behaviour of rang function calls
|
|
Off = 0, // toggle off rang style/color calls
|
|
Auto = 1, // (Default) autodect terminal and colorize if needed
|
|
Force = 2 // force ansi color output to non terminal streams
|
|
};
|
|
// Use rang::setControlMode to set rang control mode
|
|
|
|
enum class winTerm { // Windows Terminal Mode
|
|
Auto = 0, // (Default) automatically detects wheter Ansi or Native API
|
|
Ansi = 1, // Force use Ansi API
|
|
Native = 2 // Force use Native API
|
|
};
|
|
// Use rang::setWinTermMode to explicitly set terminal API for Windows
|
|
// Calling rang::setWinTermMode have no effect on other OS
|
|
|
|
namespace rang_implementation {
|
|
|
|
inline std::atomic<control> &controlMode() noexcept
|
|
{
|
|
static std::atomic<control> value(control::Auto);
|
|
return value;
|
|
}
|
|
|
|
inline std::atomic<winTerm> &winTermMode() noexcept
|
|
{
|
|
static std::atomic<winTerm> termMode(winTerm::Auto);
|
|
return termMode;
|
|
}
|
|
|
|
inline bool supportsColor() noexcept
|
|
{
|
|
#if defined(OS_LINUX) || defined(OS_MAC)
|
|
|
|
static const bool result = [] {
|
|
const char *Terms[]
|
|
= { "ansi", "color", "console", "cygwin", "gnome",
|
|
"konsole", "kterm", "linux", "msys", "putty",
|
|
"rxvt", "screen", "vt100", "xterm" };
|
|
|
|
const char *env_p = std::getenv("TERM");
|
|
if (env_p == nullptr) {
|
|
return false;
|
|
}
|
|
return std::any_of(std::begin(Terms), std::end(Terms),
|
|
[&](const char *term) {
|
|
return std::strstr(env_p, term) != nullptr;
|
|
});
|
|
}();
|
|
|
|
#elif defined(OS_WIN)
|
|
// All windows versions support colors through native console methods
|
|
static constexpr bool result = true;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
#ifdef OS_WIN
|
|
|
|
|
|
inline bool isMsysPty(int fd) noexcept
|
|
{
|
|
// Dynamic load for binary compability with old Windows
|
|
const auto ptrGetFileInformationByHandleEx
|
|
= reinterpret_cast<decltype(&GetFileInformationByHandleEx)>(
|
|
GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
|
|
"GetFileInformationByHandleEx"));
|
|
if (!ptrGetFileInformationByHandleEx) {
|
|
return false;
|
|
}
|
|
|
|
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
return false;
|
|
}
|
|
|
|
// Check that it's a pipe:
|
|
if (GetFileType(h) != FILE_TYPE_PIPE) {
|
|
return false;
|
|
}
|
|
|
|
// POD type is binary compatible with FILE_NAME_INFO from WinBase.h
|
|
// It have the same alignment and used to avoid UB in caller code
|
|
struct MY_FILE_NAME_INFO {
|
|
DWORD FileNameLength;
|
|
WCHAR FileName[MAX_PATH];
|
|
};
|
|
|
|
auto pNameInfo = std::unique_ptr<MY_FILE_NAME_INFO>(
|
|
new (std::nothrow) MY_FILE_NAME_INFO());
|
|
if (!pNameInfo) {
|
|
return false;
|
|
}
|
|
|
|
// Check pipe name is template of
|
|
// {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX
|
|
if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(),
|
|
sizeof(MY_FILE_NAME_INFO))) {
|
|
return false;
|
|
}
|
|
std::wstring name(pNameInfo->FileName, pNameInfo->FileNameLength / sizeof(WCHAR));
|
|
if ((name.find(L"msys-") == std::wstring::npos
|
|
&& name.find(L"cygwin-") == std::wstring::npos)
|
|
|| name.find(L"-pty") == std::wstring::npos) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
inline bool g_is_forced_tty = std::getenv("IS_TTY");
|
|
|
|
inline bool isTerminal(const std::streambuf *osbuf) noexcept
|
|
{
|
|
if (g_is_forced_tty) {
|
|
return g_is_forced_tty;
|
|
}
|
|
|
|
using std::cerr;
|
|
using std::clog;
|
|
using std::cout;
|
|
#if defined(OS_LINUX) || defined(OS_MAC)
|
|
if (osbuf == cout.rdbuf()) {
|
|
static const bool cout_term = isatty(fileno(stdout)) != 0;
|
|
return cout_term;
|
|
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
|
static const bool cerr_term = isatty(fileno(stderr)) != 0;
|
|
return cerr_term;
|
|
}
|
|
#elif defined(OS_WIN)
|
|
if (osbuf == cout.rdbuf()) {
|
|
static const bool cout_term
|
|
= (_isatty(_fileno(stdout)) || isMsysPty(_fileno(stdout)));
|
|
return cout_term;
|
|
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
|
static const bool cerr_term
|
|
= (_isatty(_fileno(stderr)) || isMsysPty(_fileno(stderr)));
|
|
return cerr_term;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
using enableStd = typename std::enable_if<
|
|
std::is_same<T, rang::style>::value || std::is_same<T, rang::fg>::value
|
|
|| std::is_same<T, rang::bg>::value || std::is_same<T, rang::fgB>::value
|
|
|| std::is_same<T, rang::bgB>::value,
|
|
std::ostream &>::type;
|
|
|
|
|
|
#ifdef OS_WIN
|
|
|
|
struct SGR { // Select Graphic Rendition parameters for Windows console
|
|
BYTE fgColor; // foreground color (0-15) lower 3 rgb bits + intense bit
|
|
BYTE bgColor; // background color (0-15) lower 3 rgb bits + intense bit
|
|
BYTE bold; // emulated as FOREGROUND_INTENSITY bit
|
|
BYTE underline; // emulated as BACKGROUND_INTENSITY bit
|
|
BOOLEAN inverse; // swap foreground/bold & background/underline
|
|
BOOLEAN conceal; // set foreground/bold to background/underline
|
|
};
|
|
|
|
enum class AttrColor : BYTE { // Color attributes for console screen buffer
|
|
black = 0,
|
|
red = 4,
|
|
green = 2,
|
|
yellow = 6,
|
|
blue = 1,
|
|
magenta = 5,
|
|
cyan = 3,
|
|
gray = 7
|
|
};
|
|
|
|
inline HANDLE getConsoleHandle(const std::streambuf *osbuf) noexcept
|
|
{
|
|
if (osbuf == std::cout.rdbuf()) {
|
|
static const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
return hStdout;
|
|
} else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) {
|
|
static const HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
|
|
return hStderr;
|
|
}
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
inline bool setWinTermAnsiColors(const std::streambuf *osbuf) noexcept
|
|
{
|
|
HANDLE h = getConsoleHandle(osbuf);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
return false;
|
|
}
|
|
DWORD dwMode = 0;
|
|
if (!GetConsoleMode(h, &dwMode)) {
|
|
return false;
|
|
}
|
|
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
if (!SetConsoleMode(h, dwMode)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline bool supportsAnsi(const std::streambuf *osbuf) noexcept
|
|
{
|
|
using std::cerr;
|
|
using std::clog;
|
|
using std::cout;
|
|
if (osbuf == cout.rdbuf()) {
|
|
static const bool cout_ansi
|
|
= (isMsysPty(_fileno(stdout)) || setWinTermAnsiColors(osbuf));
|
|
return cout_ansi;
|
|
} else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
|
|
static const bool cerr_ansi
|
|
= (isMsysPty(_fileno(stderr)) || setWinTermAnsiColors(osbuf));
|
|
return cerr_ansi;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline const SGR &defaultState() noexcept
|
|
{
|
|
static const SGR defaultSgr = []() -> SGR {
|
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
|
|
&info)
|
|
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE),
|
|
&info)) {
|
|
attrib = info.wAttributes;
|
|
}
|
|
SGR sgr = { 0, 0, 0, 0, FALSE, FALSE };
|
|
sgr.fgColor = attrib & 0x0F;
|
|
sgr.bgColor = (attrib & 0xF0) >> 4;
|
|
return sgr;
|
|
}();
|
|
return defaultSgr;
|
|
}
|
|
|
|
inline BYTE ansi2attr(BYTE rgb) noexcept
|
|
{
|
|
static const AttrColor rev[8]
|
|
= { AttrColor::black, AttrColor::red, AttrColor::green,
|
|
AttrColor::yellow, AttrColor::blue, AttrColor::magenta,
|
|
AttrColor::cyan, AttrColor::gray };
|
|
return static_cast<BYTE>(rev[rgb]);
|
|
}
|
|
|
|
inline void setWinSGR(rang::bg col, SGR &state) noexcept
|
|
{
|
|
if (col != rang::bg::reset) {
|
|
state.bgColor = ansi2attr(static_cast<BYTE>(col) - 40);
|
|
} else {
|
|
state.bgColor = defaultState().bgColor;
|
|
}
|
|
}
|
|
|
|
inline void setWinSGR(rang::fg col, SGR &state) noexcept
|
|
{
|
|
if (col != rang::fg::reset) {
|
|
state.fgColor = ansi2attr(static_cast<BYTE>(col) - 30);
|
|
} else {
|
|
state.fgColor = defaultState().fgColor;
|
|
}
|
|
}
|
|
|
|
inline void setWinSGR(rang::bgB col, SGR &state) noexcept
|
|
{
|
|
state.bgColor = (BACKGROUND_INTENSITY >> 4)
|
|
| ansi2attr(static_cast<BYTE>(col) - 100);
|
|
}
|
|
|
|
inline void setWinSGR(rang::fgB col, SGR &state) noexcept
|
|
{
|
|
state.fgColor
|
|
= FOREGROUND_INTENSITY | ansi2attr(static_cast<BYTE>(col) - 90);
|
|
}
|
|
|
|
inline void setWinSGR(rang::style style, SGR &state) noexcept
|
|
{
|
|
switch (style) {
|
|
case rang::style::reset: state = defaultState(); break;
|
|
case rang::style::bold: state.bold = FOREGROUND_INTENSITY; break;
|
|
case rang::style::underline:
|
|
case rang::style::blink:
|
|
state.underline = BACKGROUND_INTENSITY;
|
|
break;
|
|
case rang::style::reversed: state.inverse = TRUE; break;
|
|
case rang::style::conceal: state.conceal = TRUE; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
inline SGR ¤t_state() noexcept
|
|
{
|
|
static SGR state = defaultState();
|
|
return state;
|
|
}
|
|
|
|
inline WORD SGR2Attr(const SGR &state) noexcept
|
|
{
|
|
WORD attrib = 0;
|
|
if (state.conceal) {
|
|
if (state.inverse) {
|
|
attrib = (state.fgColor << 4) | state.fgColor;
|
|
if (state.bold)
|
|
attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
|
|
} else {
|
|
attrib = (state.bgColor << 4) | state.bgColor;
|
|
if (state.underline)
|
|
attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
|
|
}
|
|
} else if (state.inverse) {
|
|
attrib = (state.fgColor << 4) | state.bgColor;
|
|
if (state.bold) attrib |= BACKGROUND_INTENSITY;
|
|
if (state.underline) attrib |= FOREGROUND_INTENSITY;
|
|
} else {
|
|
attrib = state.fgColor | (state.bgColor << 4) | state.bold
|
|
| state.underline;
|
|
}
|
|
return attrib;
|
|
}
|
|
|
|
template <typename T>
|
|
inline void setWinColorAnsi(std::ostream &os, T const value)
|
|
{
|
|
os << "\033[" << static_cast<int>(value) << "m";
|
|
}
|
|
|
|
template <typename T>
|
|
inline void setWinColorNative(std::ostream &os, T const value)
|
|
{
|
|
const HANDLE h = getConsoleHandle(os.rdbuf());
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
setWinSGR(value, current_state());
|
|
// Out all buffered text to console with previous settings:
|
|
os.flush();
|
|
SetConsoleTextAttribute(h, SGR2Attr(current_state()));
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
inline enableStd<T> setColor(std::ostream &os, T const value)
|
|
{
|
|
if (winTermMode() == winTerm::Auto) {
|
|
if (supportsAnsi(os.rdbuf())) {
|
|
setWinColorAnsi(os, value);
|
|
} else {
|
|
setWinColorNative(os, value);
|
|
}
|
|
} else if (winTermMode() == winTerm::Ansi) {
|
|
setWinColorAnsi(os, value);
|
|
} else {
|
|
setWinColorNative(os, value);
|
|
}
|
|
return os;
|
|
}
|
|
#else
|
|
template <typename T>
|
|
inline enableStd<T> setColor(std::ostream &os, T const value)
|
|
{
|
|
return os << "\033[" << static_cast<int>(value) << "m";
|
|
}
|
|
#endif
|
|
} // namespace rang_implementation
|
|
|
|
template <typename T>
|
|
inline rang_implementation::enableStd<T> operator<<(std::ostream &os,
|
|
const T value)
|
|
{
|
|
const control option = rang_implementation::controlMode();
|
|
switch (option) {
|
|
case control::Auto:
|
|
return rang_implementation::supportsColor()
|
|
&& rang_implementation::isTerminal(os.rdbuf())
|
|
? rang_implementation::setColor(os, value)
|
|
: os;
|
|
case control::Force: return rang_implementation::setColor(os, value);
|
|
default: return os;
|
|
}
|
|
}
|
|
|
|
inline void setWinTermMode(const rang::winTerm value) noexcept
|
|
{
|
|
rang_implementation::winTermMode() = value;
|
|
}
|
|
|
|
inline void setControlMode(const control value) noexcept
|
|
{
|
|
rang_implementation::controlMode() = value;
|
|
}
|
|
|
|
} // namespace rang
|
|
|
|
#undef OS_LINUX
|
|
#undef OS_WIN
|
|
#undef OS_MAC
|
|
|
|
#endif /* ifndef RANG_DOT_HPP */
|