/*! \file stringutil.cpp * \brief String utility functions. * */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include "core.h" extern "C" { #include "color_ops.h" } #include bool g_no_flash = false; bool g_space_compress = true; UTF8 *DCL_CDECL tprintf(const UTF8 *fmt,...) { static UTF8 buff[LBUF_SIZE]; va_list ap; va_start(ap, fmt); mux_vsnprintf(buff, LBUF_SIZE, fmt, ap); va_end(ap); return buff; } void DCL_CDECL safe_tprintf_str(UTF8 *str, UTF8 **bp, const UTF8 *fmt,...) { va_list ap; va_start(ap, fmt); size_t nAvailable = LBUF_SIZE - (*bp - str); size_t len = mux_vsnprintf(*bp, static_cast(nAvailable), fmt, ap); va_end(ap); *bp += len; } #define PCRE2_CODE_UNIT_WIDTH 8 #include using namespace std; const bool mux_isprint_ascii[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_isprint_cp437[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; const bool mux_isprint_latin1[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 7 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, // 8 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; const bool mux_isprint_latin2[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; const bool mux_isdigit[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_isxdigit[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_isazAZ[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_isalnum[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_isupper_ascii[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_islower_ascii[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool mux_isspace[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // Characters which should be escaped for the secure() // function: '%$\[](){},;'. // const bool mux_issecure[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // Characters which should be escaped for the escape() // function: '%\[]{};,()^$'. // const bool mux_isescape[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const bool ANSI_TokenTerminatorTable[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 5 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const unsigned char mux_hex2dec[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 3 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const unsigned char mux_toupper_ascii[UCHAR_MAX+1] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 4 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 6 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, // 8 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, // 9 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // A 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // B 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, // C 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, // D 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, // E 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF // F }; const unsigned char mux_tolower_ascii[UCHAR_MAX+1] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 1 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 2 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 3 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 4 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, // 5 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 6 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 7 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, // 8 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, // 9 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // A 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // B 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, // C 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, // D 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, // E 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF // F }; // This will help decode UTF-8 sequences. // // 0xxxxxxx ==> 00000000-01111111 ==> 00-7F 1 byte sequence. // 10xxxxxx ==> 10000000-10111111 ==> 80-BF continue // 110xxxxx ==> 11000000-11011111 ==> C0-DF 2 byte sequence. // 1110xxxx ==> 11100000-11101111 ==> E0-EF 3 byte sequence. // 11110xxx ==> 11110000-11110111 ==> F0-F7 4 byte sequence. // 11111000-11111111 illegal // // Also, RFC 3629 specifies that 0xC0, 0xC1, and 0xF5-0xFF never // appear in a valid sequence. // // The first byte gives the length of a sequence (UTF8_SIZE1 - UTF8_SIZE4). // Bytes in the middle of a sequence map to UTF8_CONTINUE. Bytes which should // not appear map to UTF8_ILLEGAL. // extern "C" const unsigned char utf8_FirstByte[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 8 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 9 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // A 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // B 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 // F }; // Advance within a NUL-terminated UTF-8 string without stepping past '\0'. // Invalid lead bytes are treated as single-byte units. static size_t utf8_advance_nul(const UTF8 *p) { if (nullptr == p || '\0' == *p) { return 0; } size_t n = utf8_FirstByte[static_cast(*p)]; if (n >= UTF8_CONTINUE) { n = UTF8_SIZE1; } for (size_t i = 1; i < n; i++) { if ('\0' == p[i] || UTF8_CONTINUE != utf8_FirstByte[p[i]]) { return UTF8_SIZE1; } } UTF32 cp = utf8_decode_raw(p, n); if (!utf8_is_valid_scalar(cp, n)) { return UTF8_SIZE1; } return n; } // Advance within a UTF-8 byte span with explicit remaining length. // Invalid/truncated/illegal scalars are treated as single-byte units. static size_t utf8_advance_bounded(const UTF8 *p, size_t nAvail) { if (nullptr == p || 0 == nAvail) { return 0; } size_t n = utf8_FirstByte[static_cast(*p)]; if (n < 1 || n >= UTF8_CONTINUE || n > nAvail) { return UTF8_SIZE1; } for (size_t i = 1; i < n; i++) { if (UTF8_CONTINUE != utf8_FirstByte[static_cast(p[i])]) { return UTF8_SIZE1; } } UTF32 cp = utf8_decode_raw(p, n); if (!utf8_is_valid_scalar(cp, n)) { return UTF8_SIZE1; } return n; } // The following table maps cp437 characters to their corresponding // UTF8 sequences. // const UTF8 *cp437_utf8[256] = { T("\xE2\x80\x87"), T("\xE2\x98\xBA"), T("\xE2\x98\xBB"), T("\xE2\x99\xA5"), T("\xE2\x99\xA6"), T("\xE2\x99\xA3"), T("\xE2\x99\xA0"), T("\xE2\x80\xA2"), T("\xE2\x97\x98"), T("\xE2\x97\x8B"), T("\xE2\x97\x99"), T("\xE2\x99\x82"), T("\xE2\x99\x80"), T("\xE2\x99\xAA"), T("\xE2\x99\xAB"), T("\xE2\x98\xBC"), T("\xE2\x96\xBA"), T("\xE2\x97\x84"), T("\xE2\x86\x95"), T("\xE2\x80\xBC"), T("\xC2\xB6"), T("\xC2\xA7"), T("\xE2\x96\xAC"), T("\xE2\x86\xA8"), T("\xE2\x86\x91"), T("\xE2\x86\x93"), T("\xE2\x86\x92"), T("\xE2\x86\x90"), T("\xE2\x88\x9F"), T("\xE2\x86\x94"), T("\xE2\x96\xB2"), T("\xE2\x96\xBC"), T("\x20"), T("\x21"), T("\x22"), T("\x23"), T("\x24"), T("\x25"), T("\x26"), T("\x27"), T("\x28"), T("\x29"), T("\x2A"), T("\x2B"), T("\x2C"), T("\x2D"), T("\x2E"), T("\x2F"), T("\x30"), T("\x31"), T("\x32"), T("\x33"), T("\x34"), T("\x35"), T("\x36"), T("\x37"), T("\x38"), T("\x39"), T("\x3A"), T("\x3B"), T("\x3C"), T("\x3D"), T("\x3E"), T("\x3F"), T("\x40"), T("\x41"), T("\x42"), T("\x43"), T("\x44"), T("\x45"), T("\x46"), T("\x47"), T("\x48"), T("\x49"), T("\x4A"), T("\x4B"), T("\x4C"), T("\x4D"), T("\x4E"), T("\x4F"), T("\x50"), T("\x51"), T("\x52"), T("\x53"), T("\x54"), T("\x55"), T("\x56"), T("\x57"), T("\x58"), T("\x59"), T("\x5A"), T("\x5B"), T("\x5C"), T("\x5D"), T("\x5E"), T("\x5F"), T("\x60"), T("\x61"), T("\x62"), T("\x63"), T("\x64"), T("\x65"), T("\x66"), T("\x67"), T("\x68"), T("\x69"), T("\x6A"), T("\x6B"), T("\x6C"), T("\x6D"), T("\x6E"), T("\x6F"), T("\x70"), T("\x71"), T("\x72"), T("\x73"), T("\x74"), T("\x75"), T("\x76"), T("\x77"), T("\x78"), T("\x79"), T("\x7A"), T("\x7B"), T("\x7C"), T("\x7D"), T("\x7E"), T("\x7F"), T("\xC3\x87"), T("\xC3\xBC"), T("\xC3\xA9"), T("\xC3\xA2"), T("\xC3\xA4"), T("\xC3\xA0"), T("\xC3\xA5"), T("\xC3\xA7"), T("\xC3\xAA"), T("\xC3\xAB"), T("\xC3\xA8"), T("\xC3\xAF"), T("\xC3\xAE"), T("\xC3\xAC"), T("\xC3\x84"), T("\xC3\x85"), T("\xC3\x89"), T("\xC3\xA6"), T("\xC3\x86"), T("\xC3\xB4"), T("\xC3\xB6"), T("\xC3\xB2"), T("\xC3\xBB"), T("\xC3\xB9"), T("\xC3\xBF"), T("\xC3\x96"), T("\xC3\x9C"), T("\xC2\xA2"), T("\xC2\xA3"), T("\xC2\xA5"), T("\xE2\x82\xA7"), T("\xC6\x92"), T("\xC3\xA1"), T("\xC3\xAD"), T("\xC3\xB3"), T("\xC3\xBA"), T("\xC3\xB1"), T("\xC3\x91"), T("\xC2\xAA"), T("\xC2\xBA"), T("\xC2\xBF"), T("\xE2\x8C\x90"), T("\xC2\xAC"), T("\xC2\xBD"), T("\xC2\xBC"), T("\xC2\xA1"), T("\xC2\xAB"), T("\xC2\xBB"), T("\xE2\x96\x91"), T("\xE2\x96\x92"), T("\xE2\x96\x93"), T("\xE2\x94\x82"), T("\xE2\x94\xA4"), T("\xE2\x95\xA1"), T("\xE2\x95\xA2"), T("\xE2\x95\x96"), T("\xE2\x95\x95"), T("\xE2\x95\xA3"), T("\xE2\x95\x91"), T("\xE2\x95\x97"), T("\xE2\x95\x9D"), T("\xE2\x95\x9C"), T("\xE2\x95\x9B"), T("\xE2\x94\x90"), T("\xE2\x94\x94"), T("\xE2\x94\xB4"), T("\xE2\x94\xAC"), T("\xE2\x94\x9C"), T("\xE2\x94\x80"), T("\xE2\x94\xBC"), T("\xE2\x95\x9E"), T("\xE2\x95\x9F"), T("\xE2\x95\x9A"), T("\xE2\x95\x94"), T("\xE2\x95\xA9"), T("\xE2\x95\xA6"), T("\xE2\x95\xA0"), T("\xE2\x95\x90"), T("\xE2\x95\xAC"), T("\xE2\x95\xA7"), T("\xE2\x95\xA8"), T("\xE2\x95\xA4"), T("\xE2\x95\xA5"), T("\xE2\x95\x99"), T("\xE2\x95\x98"), T("\xE2\x95\x92"), T("\xE2\x95\x93"), T("\xE2\x95\xAB"), T("\xE2\x95\xAA"), T("\xE2\x94\x98"), T("\xE2\x94\x8C"), T("\xE2\x96\x88"), T("\xE2\x96\x84"), T("\xE2\x96\x8C"), T("\xE2\x96\x90"), T("\xE2\x96\x80"), T("\xCE\xB1"), T("\xC3\x9F"), T("\xCE\x93"), T("\xCF\x80"), T("\xCE\xA3"), T("\xCF\x83"), T("\xC2\xB5"), T("\xCF\x84"), T("\xCE\xA6"), T("\xCE\x98"), T("\xCE\xA9"), T("\xCE\xB4"), T("\xE2\x88\x9E"), T("\xCF\x86"), T("\xCE\xB5"), T("\xE2\x88\xA9"), T("\xE2\x89\xA1"), T("\xC2\xB1"), T("\xE2\x89\xA5"), T("\xE2\x89\xA4"), T("\xE2\x8C\xA0"), T("\xE2\x8C\xA1"), T("\xC3\xB7"), T("\xE2\x89\x88"), T("\xC2\xB0"), T("\xE2\x88\x99"), T("\xC2\xB7"), T("\xE2\x88\x9A"), T("\xE2\x81\xBF"), T("\xC2\xB2"), T("\xE2\x96\xA0"), T("\xC2\xA0"), }; // The following table maps existing 8-bit characters to their corresponding // UTF8 sequences. // const UTF8 *latin1_utf8[256] = { T(""), T("\x01"), T("\x02"), T("\x03"), T("\x04"), T("\x05"), T("\x06"), T("\x07"), T("\x08"), T("\x09"), T("\x0A"), T("\x0B"), T("\x0C"), T("\x0D"), T("\x0E"), T("\x0F"), T("\x10"), T("\x11"), T("\x12"), T("\x13"), T("\x14"), T("\x15"), T("\x16"), T("\x17"), T("\x18"), T("\x19"), T("\x1A"), T("\x1B"), T("\x1C"), T("\x1D"), T("\x1E"), T("\x1F"), T("\x20"), T("\x21"), T("\x22"), T("\x23"), T("\x24"), T("\x25"), T("\x26"), T("\x27"), T("\x28"), T("\x29"), T("\x2A"), T("\x2B"), T("\x2C"), T("\x2D"), T("\x2E"), T("\x2F"), T("\x30"), T("\x31"), T("\x32"), T("\x33"), T("\x34"), T("\x35"), T("\x36"), T("\x37"), T("\x38"), T("\x39"), T("\x3A"), T("\x3B"), T("\x3C"), T("\x3D"), T("\x3E"), T("\x3F"), T("\x40"), T("\x41"), T("\x42"), T("\x43"), T("\x44"), T("\x45"), T("\x46"), T("\x47"), T("\x48"), T("\x49"), T("\x4A"), T("\x4B"), T("\x4C"), T("\x4D"), T("\x4E"), T("\x4F"), T("\x50"), T("\x51"), T("\x52"), T("\x53"), T("\x54"), T("\x55"), T("\x56"), T("\x57"), T("\x58"), T("\x59"), T("\x5A"), T("\x5B"), T("\x5C"), T("\x5D"), T("\x5E"), T("\x5F"), T("\x60"), T("\x61"), T("\x62"), T("\x63"), T("\x64"), T("\x65"), T("\x66"), T("\x67"), T("\x68"), T("\x69"), T("\x6A"), T("\x6B"), T("\x6C"), T("\x6D"), T("\x6E"), T("\x6F"), T("\x70"), T("\x71"), T("\x72"), T("\x73"), T("\x74"), T("\x75"), T("\x76"), T("\x77"), T("\x78"), T("\x79"), T("\x7A"), T("\x7B"), T("\x7C"), T("\x7D"), T("\x7E"), T("\x7F"), T("\xE2\x82\xAC"), T("\xEF\xBF\xBD"), T("\xE2\x80\x9A"), T("\xC6\x92"), T("\xE2\x80\x9E"), T("\xE2\x80\xA6"), T("\xE2\x80\xA0"), T("\xE2\x80\xA1"), T("\xCB\x86"), T("\xE2\x80\xB0"), T("\xC5\xA0"), T("\xE2\x80\xB9"), T("\xC5\x92"), T("\xEF\xBF\xBD"), T("\xC5\xBD"), T("\xEF\xBF\xBD"), T("\xEF\xBF\xBD"), T("\xE2\x80\x98"), T("\xE2\x80\x99"), T("\xE2\x80\x9C"), T("\xE2\x80\x9D"), T("\xE2\x80\xA2"), T("\xE2\x80\x93"), T("\xE2\x80\x94"), T("\xCB\x9C"), T("\xE2\x84\xA2"), T("\xC5\xA1"), T("\xE2\x80\xBA"), T("\xC5\x93"), T("\xEF\xBF\xBD"), T("\xC5\xBE"), T("\xC5\xB8"), T("\xC2\xA0"), T("\xC2\xA1"), T("\xC2\xA2"), T("\xC2\xA3"), T("\xC2\xA4"), T("\xC2\xA5"), T("\xC2\xA6"), T("\xC2\xA7"), T("\xC2\xA8"), T("\xC2\xA9"), T("\xC2\xAA"), T("\xC2\xAB"), T("\xC2\xAC"), T("\xC2\xAD"), T("\xC2\xAE"), T("\xC2\xAF"), T("\xC2\xB0"), T("\xC2\xB1"), T("\xC2\xB2"), T("\xC2\xB3"), T("\xC2\xB4"), T("\xC2\xB5"), T("\xC2\xB6"), T("\xC2\xB7"), T("\xC2\xB8"), T("\xC2\xB9"), T("\xC2\xBA"), T("\xC2\xBB"), T("\xC2\xBC"), T("\xC2\xBD"), T("\xC2\xBE"), T("\xC2\xBF"), T("\xC3\x80"), T("\xC3\x81"), T("\xC3\x82"), T("\xC3\x83"), T("\xC3\x84"), T("\xC3\x85"), T("\xC3\x86"), T("\xC3\x87"), T("\xC3\x88"), T("\xC3\x89"), T("\xC3\x8A"), T("\xC3\x8B"), T("\xC3\x8C"), T("\xC3\x8D"), T("\xC3\x8E"), T("\xC3\x8F"), T("\xC3\x90"), T("\xC3\x91"), T("\xC3\x92"), T("\xC3\x93"), T("\xC3\x94"), T("\xC3\x95"), T("\xC3\x96"), T("\xC3\x97"), T("\xC3\x98"), T("\xC3\x99"), T("\xC3\x9A"), T("\xC3\x9B"), T("\xC3\x9C"), T("\xC3\x9D"), T("\xC3\x9E"), T("\xC3\x9F"), T("\xC3\xA0"), T("\xC3\xA1"), T("\xC3\xA2"), T("\xC3\xA3"), T("\xC3\xA4"), T("\xC3\xA5"), T("\xC3\xA6"), T("\xC3\xA7"), T("\xC3\xA8"), T("\xC3\xA9"), T("\xC3\xAA"), T("\xC3\xAB"), T("\xC3\xAC"), T("\xC3\xAD"), T("\xC3\xAE"), T("\xC3\xAF"), T("\xC3\xB0"), T("\xC3\xB1"), T("\xC3\xB2"), T("\xC3\xB3"), T("\xC3\xB4"), T("\xC3\xB5"), T("\xC3\xB6"), T("\xC3\xB7"), T("\xC3\xB8"), T("\xC3\xB9"), T("\xC3\xBA"), T("\xC3\xBB"), T("\xC3\xBC"), T("\xC3\xBD"), T("\xC3\xBE"), T("\xC3\xBF"), }; // The following table maps latin2 characters to their corresponding // UTF8 sequences. // const UTF8 *latin2_utf8[256] = { T(""), T("\x01"), T("\x02"), T("\x03"), T("\x04"), T("\x05"), T("\x06"), T("\x07"), T("\x08"), T("\x09"), T("\x0A"), T("\x0B"), T("\x0C"), T("\x0D"), T("\x0E"), T("\x0F"), T("\x10"), T("\x11"), T("\x12"), T("\x13"), T("\x14"), T("\x15"), T("\x16"), T("\x17"), T("\x18"), T("\x19"), T("\x1A"), T("\x1B"), T("\x1C"), T("\x1D"), T("\x1E"), T("\x1F"), T("\x20"), T("\x21"), T("\x22"), T("\x23"), T("\x24"), T("\x25"), T("\x26"), T("\x27"), T("\x28"), T("\x29"), T("\x2A"), T("\x2B"), T("\x2C"), T("\x2D"), T("\x2E"), T("\x2F"), T("\x30"), T("\x31"), T("\x32"), T("\x33"), T("\x34"), T("\x35"), T("\x36"), T("\x37"), T("\x38"), T("\x39"), T("\x3A"), T("\x3B"), T("\x3C"), T("\x3D"), T("\x3E"), T("\x3F"), T("\x40"), T("\x41"), T("\x42"), T("\x43"), T("\x44"), T("\x45"), T("\x46"), T("\x47"), T("\x48"), T("\x49"), T("\x4A"), T("\x4B"), T("\x4C"), T("\x4D"), T("\x4E"), T("\x4F"), T("\x50"), T("\x51"), T("\x52"), T("\x53"), T("\x54"), T("\x55"), T("\x56"), T("\x57"), T("\x58"), T("\x59"), T("\x5A"), T("\x5B"), T("\x5C"), T("\x5D"), T("\x5E"), T("\x5F"), T("\x60"), T("\x61"), T("\x62"), T("\x63"), T("\x64"), T("\x65"), T("\x66"), T("\x67"), T("\x68"), T("\x69"), T("\x6A"), T("\x6B"), T("\x6C"), T("\x6D"), T("\x6E"), T("\x6F"), T("\x70"), T("\x71"), T("\x72"), T("\x73"), T("\x74"), T("\x75"), T("\x76"), T("\x77"), T("\x78"), T("\x79"), T("\x7A"), T("\x7B"), T("\x7C"), T("\x7D"), T("\x7E"), T("\x7F"), T("\xC2\x80"), T("\xC2\x81"), T("\xC2\x82"), T("\xC2\x83"), T("\xC2\x84"), T("\xC2\x85"), T("\xC2\x86"), T("\xC2\x87"), T("\xC2\x88"), T("\xC2\x89"), T("\xC2\x8A"), T("\xC2\x8B"), T("\xC2\x8C"), T("\xC2\x8D"), T("\xC2\x8E"), T("\xC2\x8F"), T("\xC2\x90"), T("\xC2\x91"), T("\xC2\x92"), T("\xC2\x93"), T("\xC2\x94"), T("\xC2\x95"), T("\xC2\x96"), T("\xC2\x97"), T("\xC2\x98"), T("\xC2\x99"), T("\xC2\x9A"), T("\xC2\x9B"), T("\xC2\x9C"), T("\xC2\x9D"), T("\xC2\x9E"), T("\xC2\x9F"), T("\xC2\xA0"), T("\xC4\x84"), T("\xCB\x98"), T("\xC5\x81"), T("\xC2\xA4"), T("\xC4\xBD"), T("\xC5\x9A"), T("\xC2\xA7"), T("\xC2\xA8"), T("\xC5\xA0"), T("\xC5\x9E"), T("\xC5\xA4"), T("\xC5\xB9"), T("\xC2\xAD"), T("\xC5\xBD"), T("\xC5\xBB"), T("\xC2\xB0"), T("\xC4\x85"), T("\xCB\x9B"), T("\xC5\x82"), T("\xC2\xB4"), T("\xC4\xBE"), T("\xC5\x9B"), T("\xCB\x87"), T("\xC2\xB8"), T("\xC5\xA1"), T("\xC5\x9F"), T("\xC5\xA5"), T("\xC5\xBA"), T("\xCB\x9D"), T("\xC5\xBE"), T("\xC5\xBC"), T("\xC5\x94"), T("\xC3\x81"), T("\xC3\x82"), T("\xC4\x82"), T("\xC3\x84"), T("\xC4\xB9"), T("\xC4\x86"), T("\xC3\x87"), T("\xC4\x8C"), T("\xC3\x89"), T("\xC4\x98"), T("\xC3\x8B"), T("\xC4\x9A"), T("\xC3\x8D"), T("\xC3\x8E"), T("\xC4\x8E"), T("\xC4\x90"), T("\xC5\x83"), T("\xC5\x87"), T("\xC3\x93"), T("\xC3\x94"), T("\xC5\x90"), T("\xC3\x96"), T("\xC3\x97"), T("\xC5\x98"), T("\xC5\xAE"), T("\xC3\x9A"), T("\xC5\xB0"), T("\xC3\x9C"), T("\xC3\x9D"), T("\xC5\xA2"), T("\xC3\x9F"), T("\xC5\x95"), T("\xC3\xA1"), T("\xC3\xA2"), T("\xC4\x83"), T("\xC3\xA4"), T("\xC4\xBA"), T("\xC4\x87"), T("\xC3\xA7"), T("\xC4\x8D"), T("\xC3\xA9"), T("\xC4\x99"), T("\xC3\xAB"), T("\xC4\x9B"), T("\xC3\xAD"), T("\xC3\xAE"), T("\xC4\x8F"), T("\xC4\x91"), T("\xC5\x84"), T("\xC5\x88"), T("\xC3\xB3"), T("\xC3\xB4"), T("\xC5\x91"), T("\xC3\xB6"), T("\xC3\xB7"), T("\xC5\x99"), T("\xC5\xAF"), T("\xC3\xBA"), T("\xC5\xB1"), T("\xC3\xBC"), T("\xC3\xBD"), T("\xC5\xA3"), T("\xCB\x99"), }; /*! \brief Validates UTF8 string and returns number of code points contained therein. * * \param pString UTF8 string. * \param nString Resulting number of code points in pString. * \return true for valid, false for invalid. */ bool utf8_strlen(const UTF8 *pString, size_t &nString) { nString = 0; int i = 0; while ('\0' != pString[i]) { unsigned char t = utf8_FirstByte[pString[i]]; if (UTF8_CONTINUE <= t) { return false; } int j; for (j = i + 1; j < i + t; j++) { if ( '\0' == pString[j] || UTF8_CONTINUE != utf8_FirstByte[pString[j]]) { return false; } } UTF32 cp = utf8_decode_raw(pString + i, t); if (!utf8_is_valid_scalar(cp, t)) { return false; } nString++; i = i + t; } return true; } bool utf8_strlen(const UTF8 *pString, mux_cursor &nString) { LBUF_OFFSET nPoints = 0; LBUF_OFFSET nBytes = 0; while ('\0' != pString[nBytes]) { unsigned char t = utf8_FirstByte[pString[nBytes]]; if (UTF8_CONTINUE <= t) { nString(nBytes, nPoints); return false; } int j; for (j = nBytes + 1; j < nBytes + t; j++) { if ( '\0' == pString[j] || UTF8_CONTINUE != utf8_FirstByte[pString[j]]) { nString(nBytes, nPoints); return false; } } UTF32 cp = utf8_decode_raw(pString + nBytes, t); if (!utf8_is_valid_scalar(cp, t)) { nString(nBytes, nPoints); return false; } nPoints++; nBytes = nBytes + t; } nString(nBytes, nPoints); return true; } /*! \brief Convert UTF8 to ASCII with '?' for all unsupported characters. * * \param pString UTF8 string. * \return Equivalent string in ASCII codeset. */ const UTF8 *ConvertToAscii(const UTF8 *pString) { static UTF8 buffer[2*LBUF_SIZE]; UTF8 *q = buffer; while ('\0' != *pString) { *q++ = static_cast(co_dfa_ascii(pString)); pString += utf8_advance_nul(pString); } *q = '\0'; return buffer; } /*! \brief Convert UTF8 to cp437 with '?' for all unsupported characters. * * \param pString UTF8 string. * \return Equivalent string in latin1 codeset. */ const UTF8 *ConvertToCp437(const UTF8 *pString) { static UTF8 buffer[2*LBUF_SIZE]; UTF8 *q = buffer; while ( '\0' != *pString && q < buffer + sizeof(buffer) - 1) { const UTF8 *p = pString; int iState = TR_CP437_START_STATE; do { UTF8 ch = *p++; unsigned char iColumn = tr_cp437_itt[static_cast(ch)]; unsigned short iOffset = tr_cp437_sot[iState]; for (;;) { int y = tr_cp437_sbt[iOffset]; if (y < 128) { // RUN phrase. // if (iColumn < y) { iState = tr_cp437_sbt[iOffset+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset += 2; } } else { // COPY phrase. // y = 256-y; if (iColumn < y) { iState = tr_cp437_sbt[iOffset+iColumn+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset = static_cast(iOffset + y + 1); } } } } while (iState < TR_CP437_ACCEPTING_STATES_START); *q++ = static_cast(iState - TR_CP437_ACCEPTING_STATES_START); pString += utf8_advance_nul(pString); } *q = '\0'; return buffer; } /*! \brief Convert UTF8 to latin1 with '?' for all unsupported characters. * * \param pString UTF8 string. * \return Equivalent string in latin1 codeset. */ const UTF8 *ConvertToLatin1(const UTF8 *pString) { static UTF8 buffer[2*LBUF_SIZE]; UTF8 *q = buffer; while ( '\0' != *pString && q < buffer + sizeof(buffer) - 1) { const UTF8 *p = pString; int iState = TR_LATIN1_START_STATE; do { UTF8 ch = *p++; unsigned char iColumn = tr_latin1_itt[static_cast(ch)]; unsigned short iOffset = tr_latin1_sot[iState]; for (;;) { int y = tr_latin1_sbt[iOffset]; if (y < 128) { // RUN phrase. // if (iColumn < y) { iState = tr_latin1_sbt[iOffset+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset += 2; } } else { // COPY phrase. // y = 256-y; if (iColumn < y) { iState = tr_latin1_sbt[iOffset+iColumn+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset = static_cast(iOffset + y + 1); } } } } while (iState < TR_LATIN1_ACCEPTING_STATES_START); *q++ = static_cast(iState - TR_LATIN1_ACCEPTING_STATES_START); pString += utf8_advance_nul(pString); } *q = '\0'; return buffer; } /*! \brief Convert UTF8 to latin2 with '?' for all unsupported characters. * * \param pString UTF8 string. * \return Equivalent string in latin1 codeset. */ const UTF8 *ConvertToLatin2(const UTF8 *pString) { static UTF8 buffer[2*LBUF_SIZE]; UTF8 *q = buffer; while ( '\0' != *pString && q < buffer + sizeof(buffer) - 1) { const UTF8 *p = pString; int iState = TR_LATIN2_START_STATE; do { UTF8 ch = *p++; unsigned char iColumn = tr_latin2_itt[static_cast(ch)]; unsigned short iOffset = tr_latin2_sot[iState]; for (;;) { int y = tr_latin2_sbt[iOffset]; if (y < 128) { // RUN phrase. // if (iColumn < y) { iState = tr_latin2_sbt[iOffset+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset += 2; } } else { // COPY phrase. // y = 256-y; if (iColumn < y) { iState = tr_latin2_sbt[iOffset+iColumn+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset = static_cast(iOffset + y + 1); } } } } while (iState < TR_LATIN2_ACCEPTING_STATES_START); *q++ = static_cast(iState - TR_LATIN2_ACCEPTING_STATES_START); pString += utf8_advance_nul(pString); } *q = '\0'; return buffer; } /*! \brief Return ConsoleWidth property for single code point. * * \param pCodePoint UTF8 string. * \return Width to allow in formatting. */ int ConsoleWidth(const UTF8 *pCodePoint) { const UTF8 *p = pCodePoint; int iState = TR_WIDTHS_START_STATE; do { unsigned char ch = *p++; unsigned char iColumn = tr_widths_itt[static_cast(ch)]; unsigned short iOffset = tr_widths_sot[iState]; for (;;) { int y = tr_widths_sbt[iOffset]; if (y < 128) { // RUN phrase. // if (iColumn < y) { iState = tr_widths_sbt[iOffset+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset += 2; } } else { // COPY phrase. // y = 256-y; if (iColumn < y) { iState = tr_widths_sbt[iOffset+iColumn+1]; break; } else { iColumn = static_cast(iColumn - y); iOffset = static_cast(iOffset + y + 1); } } } } while (iState < TR_WIDTHS_ACCEPTING_STATES_START); return (iState - TR_WIDTHS_ACCEPTING_STATES_START); } // C-callable wrapper for use from color_ops.c (libmux.so internal). // extern "C" LIBMUX_API int co_console_width(const unsigned char *pCodePoint) { return ConsoleWidth(pCodePoint); } // ColorTable — maps ASCII characters to color index flags. // const unsigned int ColorTable[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x3F // 0x40-0x47 0, 0, COLOR_INDEX_BG + COLOR_INDEX_BLUE, COLOR_INDEX_BG + COLOR_INDEX_CYAN, 0, 0, 0, COLOR_INDEX_BG + COLOR_INDEX_GREEN, // 0x48-0x4F 0, 0, 0, 0, 0, COLOR_INDEX_BG + COLOR_INDEX_MAGENTA, 0, 0, // 0x50-0x57 // 0, 0, COLOR_INDEX_BG + COLOR_INDEX_RED, 0, 0, 0, 0, COLOR_INDEX_BG + COLOR_INDEX_WHITE, // 0x58-0x5F // COLOR_INDEX_BG + COLOR_INDEX_BLACK, COLOR_INDEX_BG + COLOR_INDEX_YELLOW, 0, 0, 0, 0, 0, 0, // 0x60-0x67 0, 0, COLOR_INDEX_FG + COLOR_INDEX_BLUE, COLOR_INDEX_FG + COLOR_INDEX_CYAN, 0, 0, COLOR_INDEX_BLINK, COLOR_INDEX_FG + COLOR_INDEX_GREEN, // 0x68-0x6F // COLOR_INDEX_INTENSE, COLOR_INDEX_INVERSE, 0, 0, 0, COLOR_INDEX_FG + COLOR_INDEX_MAGENTA, COLOR_INDEX_RESET, 0, // 0x70-0x77 // 0, 0, COLOR_INDEX_FG + COLOR_INDEX_RED, 0, 0, COLOR_INDEX_UNDERLINE, 0, COLOR_INDEX_FG + COLOR_INDEX_WHITE, // 0x78-0x7F // COLOR_INDEX_FG + COLOR_INDEX_BLACK, COLOR_INDEX_FG + COLOR_INDEX_YELLOW, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF }; bool parse_rgb(size_t n, const UTF8 *p, RGB &rgb) { UTF8 ch; if ( 7 == n && '#' == p[0]) { // Look for RRGGBB in hexidecimal. // for (int i = 1; i < 7; i++) { ch = p[i]; if ( !('0' <= ch && ch <= '9') && !('a' <= ch && ch <= 'f') && !('A' <= ch && ch <= 'F')) { return false; } } rgb.r = (mux_hex2dec(p[1]) << 4) | mux_hex2dec(p[2]); rgb.g = (mux_hex2dec(p[3]) << 4) | mux_hex2dec(p[4]); rgb.b = (mux_hex2dec(p[5]) << 4) | mux_hex2dec(p[6]); return true; } int nSpaces = 0; int nDigits = 0; for (size_t i = 0; i < n; i++) { ch = p[i]; if (mux_isspace(ch)) { if ( 3 < nDigits || 0 == nDigits || 1 < nSpaces) { return false; } if (0 == nSpaces) { rgb.r = mux_atoi64(p+i-nDigits); if (rgb.r < 0 || 255 < rgb.r) { return false; } } else { rgb.g = mux_atoi64(p+i-nDigits); if (rgb.g < 0 || 255 < rgb.g) { return false; } } nDigits = 0; nSpaces++; } else if (!mux_isdigit(ch)) { return false; } else { nDigits++; } } if ( 3 < nDigits || 0 == nDigits || 2 != nSpaces) { return false; } rgb.b = mux_atoi64(p+n-nDigits-1); if (rgb.b < 0 || 255 < rgb.b) { return false; } return true; } // sRGB to CIELAB conversion (D65 illuminant) // // Pipeline: sRGB (0-255) -> linear RGB (0-1) -> XYZ (D65) -> CIELAB // Output is integer-scaled: L*100, a*100, b*100 // static inline double srgb_to_linear(int c) { double v = c / 255.0; return (v <= 0.04045) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4); } static inline double cie_f(double t) { return (t > 0.008856) ? cbrt(t) : 7.787 * t + 16.0 / 116.0; } inline void rgb2lab(RGB *rgb, LABi *labi) { double lr = srgb_to_linear(rgb->r); double lg = srgb_to_linear(rgb->g); double lb = srgb_to_linear(rgb->b); double x = (lr * 0.4124564 + lg * 0.3575761 + lb * 0.1804375) / 0.95047; double y = (lr * 0.2126729 + lg * 0.7151522 + lb * 0.0721750) / 1.00000; double z = (lr * 0.0193339 + lg * 0.1191920 + lb * 0.9503041) / 1.08883; double fx = cie_f(x), fy = cie_f(y), fz = cie_f(z); double L = 116.0 * fy - 16.0; double a = 500.0 * (fx - fy); double b = 200.0 * (fy - fz); labi->L = static_cast(L * 100.0 + 0.5); labi->a = static_cast(a * 100.0 + (a >= 0 ? 0.5 : -0.5)); labi->b = static_cast(b * 100.0 + (b >= 0 ? 0.5 : -0.5)); } inline void cs2rgb(ColorState cs, RGB *rgb) { rgb->r = static_cast((cs & 0xFF0000) >> 16); rgb->g = static_cast((cs & 0x00FF00) >> 8); rgb->b = static_cast((cs & 0x0000FF)); } inline ColorState rgb2cs(RGB *rgb) { ColorState cs; cs = (static_cast(rgb->r) << 16) | (static_cast(rgb->g) << 8) | (static_cast(rgb->b)); return cs; } // All 256 entries of the palette are included in this table, but the palette // is divided into two non-overlapping trees -- one for when xterm is supported, // and one for when it is not. // // Since elements 0 through 15 do not have dependable RGB vales, we usually // avoid using them when we have xterm support. xterm uses the values below, // but other clients do not, and these values are usually user-configurable. // However, if we are forced to map to the 16-color palette, the 16-color tree // is used. // #define PALETTE16_ROOT 2 #define PALETTE256_ROOT 72 #define PALETTE_SIZE (sizeof(palette)/sizeof(palette[0])) PALETTE_ENTRY palette[] = { { { 0, 0, 0 }, { 0, 0, 0 }, { 12, 8 }, 0, 0}, { { 187, 0, 0 }, { 3884, 6344, 5323 }, { 0, 5 }, 1, 1}, { { 0, 187, 0 }, { 6617, -6826, 6588 }, { 1, 11 }, 2, 2}, { { 187, 187, 0 }, { 7361, -1707, 7483 }, { -1, -1 }, 3, 3}, { { 0, 0, 187 }, { 2225, 6272, -8543 }, { -1, -1 }, 4, 4}, { { 187, 0, 187 }, { 4445, 7781, -4818 }, { 13, 9 }, 5, 5}, { { 0, 187, 187 }, { 6884, -3809, -1119 }, { 14, 10 }, 6, 6}, { { 187, 187, 187 }, { 7588, 0, 0 }, { -1, -1 }, 7, 7}, { { 85, 85, 85 }, { 3615, 0, 0 }, { -1, -1 }, 0, 8}, { { 255, 85, 85 }, { 6027, 6421, 3655 }, { -1, -1 }, 1, 9}, { { 85, 255, 85 }, { 8897, -7423, 6593 }, { -1, -1 }, 2, 10}, { { 255, 255, 85 }, { 9741, -1942, 7731 }, { 6, 15 }, 3, 11}, { { 85, 85, 255 }, { 4651, 5110, -8420 }, { 4, -1 }, 4, 12}, { { 255, 85, 255 }, { 6573, 8258, -5221 }, { -1, -1 }, 5, 13}, { { 85, 255, 255 }, { 9198, -4245, -1274 }, { -1, -1 }, 6, 14}, { { 255, 255, 255 }, { 10000, 0, 0 }, { 7, 3 }, 7, 15}, { { 0, 0, 0 }, { 0, 0, 0 }, { -1, -1 }, 0, 0}, { { 0, 0, 95 }, { 746, 3839, -5234 }, { -1, -1 }, 4, 4}, { { 0, 0, 135 }, { 1411, 4937, -6724 }, { 20, 54 }, 4, 4}, { { 0, 0, 175 }, { 2042, 5971, -8133 }, { -1, -1 }, 4, 4}, { { 0, 0, 215 }, { 2646, 6962, -9483 }, { 19, 55 }, 4, 4}, { { 0, 0, 255 }, { 3230, 7919, -10786 }, { -1, -1 }, 4, 4}, { { 0, 95, 0 }, { 3436, -4184, 4038 }, { -1, -1 }, 2, 2}, { { 0, 95, 95 }, { 3600, -2335, -686 }, { 16, 24 }, 6, 8}, { { 0, 95, 135 }, { 3772, -828, -2884 }, { -1, -1 }, 6, 8}, { { 0, 95, 175 }, { 4004, 805, -4908 }, { -1, -1 }, 7, 8}, { { 0, 95, 215 }, { 4290, 2423, -6767 }, { -1, -1 }, 4, 12}, { { 0, 95, 255 }, { 4618, 3961, -8484 }, { 56, 170 }, 4, 12}, { { 0, 135, 0 }, { 4867, -5373, 5185 }, { -1, -1 }, 2, 2}, { { 0, 135, 95 }, { 4968, -4147, 1287 }, { 22, 65 }, 6, 6}, { { 0, 135, 135 }, { 5078, -2998, -881 }, { -1, -1 }, 6, 6}, { { 0, 135, 175 }, { 5231, -1609, -2967 }, { -1, -1 }, 6, 6}, { { 0, 135, 215 }, { 5427, -98, -4935 }, { 31, 67 }, 7, 8}, { { 0, 135, 255 }, { 5663, 1444, -6783 }, { -1, -1 }, 4, 12}, { { 0, 175, 0 }, { 6222, -6498, 6272 }, { -1, -1 }, 2, 2}, { { 0, 175, 95 }, { 6291, -5627, 3055 }, { 245, 70 }, 2, 2}, { { 0, 175, 135 }, { 6368, -4753, 999 }, { -1, -1 }, 6, 6}, { { 0, 175, 175 }, { 6477, -3626, -1066 }, { -1, -1 }, 6, 6}, { { 0, 175, 215 }, { 6618, -2318, -3066 }, { -1, -1 }, 6, 6}, { { 0, 175, 255 }, { 6793, -902, -4979 }, { -1, -1 }, 6, 6}, { { 0, 215, 0 }, { 7520, -7577, 7313 }, { 77, 112 }, 2, 2}, { { 0, 215, 95 }, { 7571, -6924, 4642 }, { -1, -1 }, 2, 2}, { { 0, 215, 135 }, { 7628, -6244, 2736 }, { -1, -1 }, 2, 2}, { { 0, 215, 175 }, { 7710, -5332, 741 }, { -1, -1 }, 6, 6}, { { 0, 215, 215 }, { 7817, -4228, -1242 }, { 43, 80 }, 6, 6}, { { 0, 215, 255 }, { 7951, -2980, -3174 }, { 38, 81 }, 6, 6}, { { 0, 255, 0 }, { 8773, -8618, 8318 }, { -1, -1 }, 2, 10}, { { 0, 255, 95 }, { 8813, -8108, 6078 }, { -1, -1 }, 2, 10}, { { 0, 255, 135 }, { 8857, -7565, 4337 }, { 113, 155 }, 2, 10}, { { 0, 255, 175 }, { 8921, -6819, 2441 }, { -1, -1 }, 2, 10}, { { 0, 255, 215 }, { 9005, -5890, 505 }, { -1, -1 }, 6, 14}, { { 0, 255, 255 }, { 9111, -4809, -1413 }, { 50, 86 }, 6, 14}, { { 95, 0, 0 }, { 1762, 3888, 2721 }, { -1, -1 }, 1, 1}, { { 95, 0, 95 }, { 2106, 4769, -2953 }, { -1, -1 }, 5, 5}, { { 95, 0, 135 }, { 2427, 5511, -5011 }, { 17, 91 }, 5, 5}, { { 95, 0, 175 }, { 2819, 6350, -6819 }, { -1, -1 }, 4, 4}, { { 95, 0, 215 }, { 3257, 7228, -8450 }, { 18, 92 }, 4, 4}, { { 95, 0, 255 }, { 3721, 8116, -9954 }, { 21, 93 }, 4, 4}, { { 95, 95, 0 }, { 3893, -1046, 4587 }, { 29, 64 }, 3, 3}, { { 95, 95, 95 }, { 4032, 0, 0 }, { 61, 101 }, 7, 8}, { { 95, 95, 135 }, { 4179, 972, -2218 }, { 25, 26 }, 7, 8}, { { 95, 95, 175 }, { 4382, 2136, -4283 }, { 236, 68 }, 5, 8}, { { 95, 95, 215 }, { 4634, 3391, -6192 }, { 99, 134 }, 4, 12}, { { 95, 95, 255 }, { 4930, 4665, -7961 }, { -1, -1 }, 4, 12}, { { 95, 135, 0 }, { 5157, -3111, 5536 }, { 28, 100 }, 3, 3}, { { 95, 135, 95 }, { 5249, -2237, 1719 }, { -1, -1 }, 6, 8}, { { 95, 135, 135 }, { 5350, -1376, -446 }, { 30, 37 }, 7, 8}, { { 95, 135, 175 }, { 5492, -286, -2541 }, { -1, -1 }, 7, 8}, { { 95, 135, 215 }, { 5675, 952, -4526 }, { 103, 104 }, 7, 7}, { { 95, 135, 255 }, { 5895, 2267, -6396 }, { 33, 105 }, 4, 12}, { { 95, 175, 0 }, { 6424, -4820, 6517 }, { 34, 71 }, 2, 2}, { { 95, 175, 95 }, { 6490, -4117, 3349 }, { -1, -1 }, 2, 2}, { { 95, 175, 135 }, { 6562, -3396, 1301 }, { 140, 228 }, 6, 6}, { { 95, 175, 175 }, { 6666, -2446, -763 }, { -1, -1 }, 6, 6}, { { 95, 175, 215 }, { 6802, -1319, -2768 }, { 39, 75 }, 6, 6}, { { 95, 175, 255 }, { 6969, -71, -4690 }, { -1, -1 }, 7, 7}, { { 95, 215, 0 }, { 7670, -6288, 7495 }, { -1, -1 }, 2, 2}, { { 95, 215, 95 }, { 7720, -5722, 4854 }, { 41, 47 }, 2, 2}, { { 95, 215, 135 }, { 7774, -5127, 2957 }, { 42, 114 }, 2, 2}, { { 95, 215, 175 }, { 7853, -4321, 966 }, { 44, 78 }, 6, 6}, { { 95, 215, 215 }, { 7958, -3332, -1018 }, { -1, -1 }, 6, 6}, { { 95, 215, 255 }, { 8088, -2201, -2952 }, { -1, -1 }, 6, 6}, { { 95, 255, 0 }, { 8890, -7597, 8460 }, { -1, -1 }, 2, 10}, { { 95, 255, 95 }, { 8929, -7135, 6239 }, { -1, -1 }, 2, 10}, { { 95, 255, 135 }, { 8972, -6642, 4506 }, { 83, 120 }, 2, 10}, { { 95, 255, 175 }, { 9034, -5961, 2614 }, { 49, 121 }, 2, 10}, { { 95, 255, 215 }, { 9117, -5106, 680 }, { -1, -1 }, 6, 14}, { { 95, 255, 255 }, { 9221, -4104, -1238 }, { 122, 194 }, 6, 14}, { { 135, 0, 0 }, { 2717, 4993, 4014 }, { 52, 131 }, 1, 1}, { { 135, 0, 95 }, { 2936, 5573, -1590 }, { 53, 97 }, 5, 5}, { { 135, 0, 135 }, { 3158, 6124, -3792 }, { 27, 133 }, 5, 5}, { { 135, 0, 175 }, { 3449, 6804, -5761 }, { -1, -1 }, 5, 5}, { { 135, 0, 215 }, { 3795, 7565, -7543 }, { 57, 128 }, 4, 4}, { { 135, 0, 255 }, { 4180, 8371, -9179 }, { -1, -1 }, 4, 4}, { { 135, 95, 0 }, { 4327, 913, 5093 }, { -1, -1 }, 3, 3}, { { 135, 95, 95 }, { 4447, 1631, 651 }, { 243, 130 }, 7, 8}, { { 135, 95, 135 }, { 4575, 2337, -1577 }, { -1, -1 }, 7, 8}, { { 135, 95, 175 }, { 4753, 3230, -3670 }, { -1, -1 }, 5, 5}, { { 135, 95, 215 }, { 4979, 4244, -5618 }, { -1, -1 }, 5, 12}, { { 135, 95, 255 }, { 5246, 5322, -7432 }, { 63, 135 }, 4, 12}, { { 135, 135, 0 }, { 5453, -1344, 5890 }, { -1, -1 }, 3, 3}, { { 135, 135, 95 }, { 5539, -677, 2158 }, { 241, 246 }, 7, 8}, { { 135, 135, 135 }, { 5632, 0, 0 }, { -1, -1 }, 7, 7}, { { 135, 135, 175 }, { 5763, 883, -2102 }, { 32, 66 }, 7, 7}, { { 135, 135, 215 }, { 5933, 1918, -4102 }, { 69, 139 }, 7, 7}, { { 135, 135, 255 }, { 6139, 3051, -5992 }, { -1, -1 }, 5, 12}, { { 135, 175, 0 }, { 6637, -3334, 6775 }, { -1, -1 }, 3, 3}, { { 135, 175, 95 }, { 6700, -2753, 3658 }, { 106, 149 }, 3, 3}, { { 135, 175, 135 }, { 6769, -2148, 1621 }, { 73, -1 }, 7, 7}, { { 135, 175, 175 }, { 6868, -1338, -441 }, { -1, -1 }, 7, 7}, { { 135, 175, 215 }, { 6998, -359, -2451 }, { 74, 248 }, 7, 7}, { { 135, 175, 255 }, { 7157, 745, -4381 }, { 110, 212 }, 7, 7}, { { 135, 215, 0 }, { 7832, -5059, 7691 }, { 76, 46 }, 2, 2}, { { 135, 215, 95 }, { 7880, -4565, 5082 }, { 40, 142 }, 2, 2}, { { 135, 215, 135 }, { 7933, -4042, 3195 }, { -1, -1 }, 6, 6}, { { 135, 215, 175 }, { 8009, -3327, 1209 }, { 79, 116 }, 6, 6}, { { 135, 215, 215 }, { 8110, -2441, -775 }, { 45, 108 }, 6, 6}, { { 135, 215, 255 }, { 8236, -1415, -2712 }, { 146, 153 }, 7, 7}, { { 135, 255, 0 }, { 9017, -6577, 8614 }, { 82, 154 }, 2, 10}, { { 135, 255, 95 }, { 9055, -6160, 6414 }, { 84, 118 }, 2, 10}, { { 135, 255, 135 }, { 9097, -5712, 4689 }, { -1, -1 }, 2, 10}, { { 135, 255, 175 }, { 9158, -5090, 2803 }, { -1, -1 }, 6, 14}, { { 135, 255, 215 }, { 9239, -4305, 871 }, { 51, 85 }, 6, 14}, { { 135, 255, 255 }, { 9340, -3378, -1048 }, { -1, -1 }, 6, 14}, { { 175, 0, 0 }, { 3621, 6039, 5057 }, { 132, 125 }, 1, 1}, { { 175, 0, 95 }, { 3774, 6450, -244 }, { 162, 161 }, 5, 9}, { { 175, 0, 135 }, { 3935, 6865, -2513 }, { -1, -1 }, 5, 5}, { { 175, 0, 175 }, { 4155, 7407, -4586 }, { -1, -1 }, 5, 5}, { { 175, 0, 215 }, { 4426, 8046, -6485 }, { 127, -1 }, 5, 5}, { { 175, 0, 255 }, { 4741, 8752, -8236 }, { -1, -1 }, 4, 4}, { { 175, 95, 0 }, { 4864, 2733, 5703 }, { 94, -1 }, 1, 1}, { { 175, 95, 95 }, { 4965, 3235, 1454 }, { -1, -1 }, 7, 8}, { { 175, 95, 135 }, { 5075, 3748, -774 }, { 89, 88 }, 7, 8}, { { 175, 95, 175 }, { 5228, 4425, -2893 }, { 124, 204 }, 5, 5}, { { 175, 95, 215 }, { 5424, 5228, -4881 }, { 98, 141 }, 5, 5}, { { 175, 95, 255 }, { 5660, 6118, -6741 }, { -1, -1 }, 5, 12}, { { 175, 135, 0 }, { 5846, 507, 6350 }, { -1, -1 }, 3, 3}, { { 175, 135, 95 }, { 5922, 1007, 2735 }, { -1, -1 }, 7, 7}, { { 175, 135, 135 }, { 6006, 1527, 589 }, { 137, 247 }, 7, 7}, { { 175, 135, 175 }, { 6125, 2223, -1518 }, { 96, -1 }, 7, 7}, { { 175, 135, 215 }, { 6280, 3063, -3534 }, { 59, 90 }, 7, 7}, { { 175, 135, 255 }, { 6469, 4011, -5447 }, { -1, -1 }, 5, 12}, { { 175, 175, 0 }, { 6931, -1625, 7124 }, { 107, 184 }, 3, 3}, { { 175, 175, 95 }, { 6990, -1160, 4080 }, { -1, -1 }, 3, 3}, { { 175, 175, 135 }, { 7054, -669, 2058 }, { -1, -1 }, 7, 7}, { { 175, 175, 175 }, { 7147, 0, 0 }, { -1, -1 }, 7, 7}, { { 175, 175, 215 }, { 7268, 824, -2014 }, { -1, -1 }, 7, 7}, { { 175, 175, 255 }, { 7418, 1772, -3954 }, { 189, 182 }, 7, 7}, { { 175, 215, 0 }, { 8058, -3551, 7963 }, { -1, -1 }, 3, 3}, { { 175, 215, 95 }, { 8104, -3135, 5399 }, { -1, -1 }, 3, 3}, { { 175, 215, 135 }, { 8155, -2689, 3528 }, { 151, 48 }, 3, 3}, { { 175, 215, 175 }, { 8228, -2074, 1548 }, { 115, 87 }, 7, 7}, { { 175, 215, 215 }, { 8324, -1303, -434 }, { 250, 195 }, 7, 7}, { { 175, 215, 255 }, { 8445, -399, -2375 }, { -1, -1 }, 7, 7}, { { 175, 255, 0 }, { 9197, -5270, 8831 }, { -1, -1 }, 2, 10}, { { 175, 255, 95 }, { 9234, -4904, 6661 }, { 119, 227 }, 2, 10}, { { 175, 255, 135 }, { 9274, -4508, 4948 }, { -1, -1 }, 2, 10}, { { 175, 255, 175 }, { 9333, -3956, 3070 }, { -1, -1 }, 6, 14}, { { 175, 255, 215 }, { 9411, -3254, 1142 }, { 123, 159 }, 6, 14}, { { 175, 255, 255 }, { 9510, -2417, -777 }, { -1, -1 }, 6, 14}, { { 215, 0, 0 }, { 4487, 7041, 5908 }, { -1, -1 }, 1, 1}, { { 215, 0, 95 }, { 4601, 7349, 1053 }, { 160, -1 }, 1, 9}, { { 215, 0, 135 }, { 4724, 7671, -1235 }, { 126, 163 }, 5, 5}, { { 215, 0, 175 }, { 4894, 8105, -3368 }, { -1, -1 }, 5, 5}, { { 215, 0, 215 }, { 5110, 8636, -5348 }, { -1, -1 }, 5, 5}, { { 215, 0, 255 }, { 5367, 9245, -7188 }, { 129, 201 }, 5, 13}, { { 215, 95, 0 }, { 5470, 4355, 6373 }, { -1, -1 }, 1, 1}, { { 215, 95, 95 }, { 5554, 4720, 2349 }, { 169, 202 }, 1, 9}, { { 215, 95, 135 }, { 5647, 5103, 135 }, { -1, -1 }, 7, 9}, { { 215, 95, 175 }, { 5778, 5623, -2000 }, { 168, 174 }, 5, 5}, { { 215, 95, 215 }, { 5947, 6260, -4020 }, { 62, 171 }, 5, 5}, { { 215, 95, 255 }, { 6153, 6990, -5924 }, { 165, 200 }, 5, 13}, { { 215, 135, 0 }, { 6316, 2286, 6890 }, { 136, -1 }, 3, 3}, { { 215, 135, 95 }, { 6384, 2663, 3419 }, { 138, 172 }, 7, 9}, { { 215, 135, 135 }, { 6459, 3063, 1294 }, { -1, -1 }, 7, 7}, { { 215, 135, 175 }, { 6565, 3610, -813 }, { -1, -1 }, 7, 7}, { { 215, 135, 215 }, { 6704, 4286, -2843 }, { 207, 177 }, 5, 5}, { { 215, 135, 255 }, { 6875, 5069, -4779 }, { -1, -1 }, 5, 13}, { { 215, 175, 0 }, { 7296, 143, 7553 }, { 143, 179 }, 3, 3}, { { 215, 175, 95 }, { 7350, 512, 4600 }, { -1, -1 }, 3, 3}, { { 215, 175, 135 }, { 7410, 906, 2601 }, { 181, 178 }, 7, 7}, { { 215, 175, 175 }, { 7495, 1450, 549 }, { 144, 252 }, 7, 7}, { { 215, 175, 215 }, { 7608, 2132, -1468 }, { 183, 225 }, 7, 7}, { { 215, 175, 255 }, { 7748, 2932, -3418 }, { 213, 219 }, 7, 7}, { { 215, 215, 0 }, { 8347, -1895, 8306 }, { 148, -1 }, 3, 3}, { { 215, 215, 95 }, { 8390, -1549, 5801 }, { 214, 254 }, 3, 3}, { { 215, 215, 135 }, { 8438, -1177, 3949 }, { -1, -1 }, 3, 3}, { { 215, 215, 175 }, { 8507, -658, 1980 }, { 253, 220 }, 7, 7}, { { 215, 215, 215 }, { 8598, 0, 0 }, { -1, -1 }, 7, 7}, { { 215, 215, 255 }, { 8713, 781, -1944 }, { 117, 152 }, 7, 7}, { { 215, 255, 0 }, { 9430, -3767, 9110 }, { -1, -1 }, 3, 11}, { { 215, 255, 95 }, { 9465, -3451, 6978 }, { 156, 192 }, 3, 11}, { { 215, 255, 135 }, { 9504, -3109, 5283 }, { -1, -1 }, 3, 11}, { { 215, 255, 175 }, { 9561, -2628, 3414 }, { 157, -1 }, 3, 15}, { { 215, 255, 215 }, { 9636, -2012, 1491 }, { 158, 193 }, 7, 15}, { { 215, 255, 255 }, { 9730, -1272, -427 }, { -1, -1 }, 7, 15}, { { 255, 0, 0 }, { 5324, 8009, 6720 }, { -1, -1 }, 1, 1}, { { 255, 0, 95 }, { 5413, 8249, 2291 }, { 196, -1 }, 1, 9}, { { 255, 0, 135 }, { 5509, 8505, 17 }, { 205, 197 }, 5, 9}, { { 255, 0, 175 }, { 5645, 8859, -2145 }, { -1, -1 }, 5, 5}, { { 255, 0, 215 }, { 5820, 9303, -4177 }, { 164, -1 }, 5, 13}, { { 255, 0, 255 }, { 6032, 9823, -6082 }, { -1, -1 }, 5, 13}, { { 255, 95, 0 }, { 6118, 5801, 7073 }, { 166, 203 }, 1, 1}, { { 255, 95, 95 }, { 6189, 6077, 3294 }, { -1, -1 }, 1, 9}, { { 255, 95, 135 }, { 6268, 6372, 1106 }, { 167, 198 }, 1, 9}, { { 255, 95, 175 }, { 6379, 6781, -1033 }, { 199, 206 }, 5, 5}, { { 255, 95, 215 }, { 6524, 7293, -3077 }, { -1, -1 }, 5, 13}, { { 255, 95, 255 }, { 6703, 7895, -5017 }, { -1, -1 }, 5, 13}, { { 255, 135, 0 }, { 6846, 3935, 7486 }, { -1, -1 }, 1, 1}, { { 255, 135, 95 }, { 6905, 4226, 4178 }, { 216, 215 }, 1, 9}, { { 255, 135, 135 }, { 6971, 4538, 2083 }, { -1, -1 }, 1, 9}, { { 255, 135, 175 }, { 7066, 4971, -18 }, { 175, -1 }, 7, 9}, { { 255, 135, 215 }, { 7189, 5518, -2058 }, { 176, 211 }, 5, 13}, { { 255, 135, 255 }, { 7342, 6164, -4013 }, { -1, -1 }, 5, 13}, { { 255, 175, 0 }, { 7724, 1872, 8047 }, { 180, 209 }, 3, 3}, { { 255, 175, 95 }, { 7773, 2165, 5200 }, { 208, -1 }, 3, 3}, { { 255, 175, 135 }, { 7827, 2482, 3230 }, { 210, 217 }, 7, 7}, { { 255, 175, 175 }, { 7905, 2924, 1190 }, { -1, -1 }, 7, 7}, { { 255, 175, 215 }, { 8008, 3486, -827 }, { -1, -1 }, 7, 7}, { { 255, 175, 255 }, { 8137, 4155, -2786 }, { -1, -1 }, 7, 13}, { { 255, 215, 0 }, { 8693, -192, 8713 }, { 186, 229 }, 3, 11}, { { 255, 215, 95 }, { 8733, 93, 6278 }, { -1, -1 }, 3, 3}, { { 255, 215, 135 }, { 8778, 402, 4451 }, { 221, -1 }, 3, 3}, { { 255, 215, 175 }, { 8843, 836, 2496 }, { 255, 222 }, 7, 15}, { { 255, 215, 215 }, { 8928, 1392, 520 }, { -1, -1 }, 7, 15}, { { 255, 215, 255 }, { 9036, 2059, -1425 }, { 218, -1 }, 7, 15}, { { 255, 255, 0 }, { 9714, -2155, 9448 }, { 190, -1 }, 3, 11}, { { 255, 255, 95 }, { 9747, -1887, 7362 }, { 191, 226 }, 3, 11}, { { 255, 255, 135 }, { 9785, -1594, 5688 }, { 150, 251 }, 3, 11}, { { 255, 255, 175 }, { 9838, -1180, 3833 }, { -1, -1 }, 3, 11}, { { 255, 255, 215 }, { 9910, -647, 1916 }, { -1, -1 }, 7, 15}, { { 255, 255, 255 }, { 10000, 0, 0 }, { -1, -1 }, 7, 15}, { { 8, 8, 8 }, { 219, 0, 0 }, { 23, 234 }, 0, 0}, { { 18, 18, 18 }, { 546, 0, 0 }, { -1, -1 }, 0, 0}, { { 28, 28, 28 }, { 1027, 0, 0 }, { 233, 235 }, 0, 0}, { { 38, 38, 38 }, { 1516, 0, 0 }, { -1, -1 }, 0, 0}, { { 48, 48, 48 }, { 1987, 0, 0 }, { 232, 237 }, 0, 8}, { { 58, 58, 58 }, { 2442, 0, 0 }, { 60, 239 }, 0, 8}, { { 68, 68, 68 }, { 2885, 0, 0 }, { -1, -1 }, 0, 8}, { { 78, 78, 78 }, { 3318, 0, 0 }, { 238, 240 }, 0, 8}, { { 88, 88, 88 }, { 3741, 0, 0 }, { -1, -1 }, 0, 8}, { { 98, 98, 98 }, { 4155, 0, 0 }, { 58, 95 }, 7, 8}, { { 108, 108, 108 }, { 4563, 0, 0 }, { -1, -1 }, 7, 8}, { { 118, 118, 118 }, { 4964, 0, 0 }, { 242, 244 }, 7, 8}, { { 128, 128, 128 }, { 5359, 0, 0 }, { -1, -1 }, 7, 8}, { { 138, 138, 138 }, { 5748, 0, 0 }, { 102, 36 }, 7, 7}, { { 148, 148, 148 }, { 6132, 0, 0 }, { 35, 173 }, 7, 7}, { { 158, 158, 158 }, { 6511, 0, 0 }, { -1, -1 }, 7, 7}, { { 168, 168, 168 }, { 6887, 0, 0 }, { 109, 145 }, 7, 7}, { { 178, 178, 178 }, { 7257, 0, 0 }, { 111, 147 }, 7, 7}, { { 188, 188, 188 }, { 7625, 0, 0 }, { -1, -1 }, 7, 7}, { { 198, 198, 198 }, { 7988, 0, 0 }, { 249, 185 }, 7, 7}, { { 208, 208, 208 }, { 8348, 0, 0 }, { -1, -1 }, 7, 7}, { { 218, 218, 218 }, { 8705, 0, 0 }, { 188, 230 }, 7, 7}, { { 228, 228, 228 }, { 9059, 0, 0 }, { 187, 223 }, 7, 15}, { { 238, 238, 238 }, { 9410, 0, 0 }, { 224, 231 }, 7, 15}, }; // CIE76 color difference: squared Euclidean distance in CIELAB. // int64_t diff(const LABi &lab1, const LABi &lab2) { int64_t dL = static_cast(lab1.L - lab2.L); int64_t da = static_cast(lab1.a - lab2.a); int64_t db = static_cast(lab1.b - lab2.b); int64_t r = dL*dL + da*da + db*db; return r; } void NearestIndex_tree_a(int iHere, const LABi &labi, int &iBest, int64_t &rBest); void NearestIndex_tree_b(int iHere, const LABi &labi, int &iBest, int64_t &rBest); void NearestIndex_tree_L(int iHere, const LABi &labi, int &iBest, int64_t &rBest) { if (-1 == iHere) { return; } if (-1 == iBest) { iBest = iHere; rBest = diff(labi, palette[iBest].labi); } int64_t rHere = diff(labi, palette[iHere].labi); if (rHere < rBest) { iBest = iHere; rBest = rHere; } int64_t d = static_cast(labi.L - palette[iHere].labi.L); int iNearChild = (d < 0)?0:1; NearestIndex_tree_a(palette[iHere].child[iNearChild], labi, iBest, rBest); int64_t rAxis = d*d; if (rAxis < rBest) { NearestIndex_tree_a(palette[iHere].child[1-iNearChild], labi, iBest, rBest); } } void NearestIndex_tree_a(int iHere, const LABi &labi, int &iBest, int64_t &rBest) { if (-1 == iHere) { return; } if (-1 == iBest) { iBest = iHere; rBest = diff(labi, palette[iBest].labi); } int64_t rHere = diff(labi, palette[iHere].labi); if (rHere < rBest) { iBest = iHere; rBest = rHere; } int64_t d = static_cast(labi.a - palette[iHere].labi.a); int iNearChild = (d < 0)?0:1; NearestIndex_tree_b(palette[iHere].child[iNearChild], labi, iBest, rBest); int64_t rAxis = d*d; if (rAxis < rBest) { NearestIndex_tree_b(palette[iHere].child[1-iNearChild], labi, iBest, rBest); } } void NearestIndex_tree_b(int iHere, const LABi &labi, int &iBest, int64_t &rBest) { if (-1 == iHere) { return; } if (-1 == iBest) { iBest = iHere; rBest = diff(labi, palette[iBest].labi); } int64_t rHere = diff(labi, palette[iHere].labi); if (rHere < rBest) { iBest = iHere; rBest = rHere; } int64_t d = static_cast(labi.b - palette[iHere].labi.b); int iNearChild = (d < 0)?0:1; NearestIndex_tree_L(palette[iHere].child[iNearChild], labi, iBest, rBest); int64_t rAxis = d*d; if (rAxis < rBest) { NearestIndex_tree_L(palette[iHere].child[1-iNearChild], labi, iBest, rBest); } } int FindNearestPaletteEntry(RGB &rgb, bool fColor256) { LABi labi; rgb2lab(&rgb, &labi); int64_t d; int j = -1; NearestIndex_tree_L(fColor256 ? PALETTE256_ROOT : PALETTE16_ROOT, labi, j, d); return j; } int FindNearestPalette8Entry(RGB &rgb) { LABi labi; rgb2lab(&rgb, &labi); int iNearest = 0; int64_t rNearest = diff(labi, palette[0].labi); for (int i = 1; i < 8; i++) { int64_t r = diff(labi, palette[i].labi); if (r < rNearest) { rNearest = r; iNearest = i; } } return iNearest; } // C-callable wrappers for CIE97 + K-d tree nearest-color search. // extern "C" LIBMUX_API int co_nearest_xterm256(const unsigned char *rgb) { RGB c; c.r = rgb[0]; c.g = rgb[1]; c.b = rgb[2]; return FindNearestPaletteEntry(c, true); } extern "C" LIBMUX_API int co_nearest_xterm16(const unsigned char *rgb) { RGB c; c.r = rgb[0]; c.g = rgb[1]; c.b = rgb[2]; return FindNearestPaletteEntry(c, false); } #define CS_FOREGROUND UINT64_C(0x0000000001FFFFFF) #define CS_FOREGROUND_RED UINT64_C(0x0000000000FF0000) #define CS_FOREGROUND_GREEN UINT64_C(0x000000000000FF00) #define CS_FOREGROUND_BLUE UINT64_C(0x00000000000000FF) #define CS_FG_BLACK UINT64_C(0x0000000001000000) // FOREGROUND BLACK (0,0,0) #define CS_FG_RED UINT64_C(0x0000000001000001) // FOREGROUND RED (187,0,0) #define CS_FG_GREEN UINT64_C(0x0000000001000002) // FOREGROUND GREEN (0,187,0) #define CS_FG_YELLOW UINT64_C(0x0000000001000003) // FOREGROUND YELLOW (187,187,0) #define CS_FG_BLUE UINT64_C(0x0000000001000004) // FOREGROUND BLUE (0,0,187) #define CS_FG_MAGENTA UINT64_C(0x0000000001000005) // FOREGROUND MAGENTA (187,0,187) #define CS_FG_CYAN UINT64_C(0x0000000001000006) // FOREGROUND CYAN (0,187,187) #define CS_FG_WHITE UINT64_C(0x0000000001000007) // FOREGROUND WHITE (187,187,187) #define CS_FG_INDEXED UINT64_C(0x0000000001000000) #define CS_FG(x) (CS_FG_INDEXED | static_cast(x)) #define CS_FG_FIELD(x) ((x) & UINT64_C(0x0000000000FFFFFF)) #define CS_FG_FIELD_INDEXED(x) ((x) & UINT64_C(0x00000000000000FF)) #define CS_FG_DEFAULT CS_FG(NUM_FG) #define CS_BACKGROUND UINT64_C(0x01FFFFFF00000000) #define CS_BACKGROUND_RED UINT64_C(0x00FF000000000000) #define CS_BACKGROUND_GREEN UINT64_C(0x0000FF0000000000) #define CS_BACKGROUND_BLUE UINT64_C(0x000000FF00000000) #define CS_BG_BLACK UINT64_C(0x0100000000000000) // BACKGROUND BLACK (0,0,0) #define CS_BG_RED UINT64_C(0x0100000100000000) // BACKGROUND RED (187,0,0) #define CS_BG_GREEN UINT64_C(0x0100000200000000) // BACKGROUND GREEN (0,187,0) #define CS_BG_YELLOW UINT64_C(0x0100000300000000) // BACKGROUND YELLOW (187,187,0) #define CS_BG_BLUE UINT64_C(0x0100000400000000) // BACKGROUND BLUE (0,0,187) #define CS_BG_MAGENTA UINT64_C(0x0100000500000000) // BACKGROUND MAGENTA (187,0,187) #define CS_BG_CYAN UINT64_C(0x0100000600000000) // BACKGROUND CYAN (0,187,187) #define CS_BG_WHITE UINT64_C(0x0100000700000000) // BACKGROUND WHITE (187,187,187) #define CS_BG_INDEXED UINT64_C(0x0100000000000000) #define CS_BG(x) (CS_BG_INDEXED | (static_cast(x) << 32)) #define CS_BG_FIELD(x) (((x) & UINT64_C(0x00FFFFFF00000000)) >> 32) #define CS_BG_FIELD_INDEXED(x) (((x) & UINT64_C(0x000000FF00000000)) >> 32) #define CS_BG_DEFAULT CS_BG(NUM_BG) #define CS_INTENSE UINT64_C(0x0000000010000000) #define CS_INVERSE UINT64_C(0x0000000020000000) #define CS_UNDERLINE UINT64_C(0x0000000040000000) #define CS_BLINK UINT64_C(0x0000000080000000) #define CS_ATTRS UINT64_C(0x00000000F0000000) #define CS_ALLBITS UINT64_C(0x01FFFFFFF1FFFFFF) #define CS_NORMAL (CS_FG_DEFAULT|CS_BG_DEFAULT) #define CS_NOBLEED (CS_FG_WHITE|CS_BG_DEFAULT) // XTERM_FG(0) through XTERM_FG(7) is equivalent to ANSI_BLACK...ANSI_WHITE. // Even for 256-color-capable clients, the latter are used instead of the former. // Similiarly for XTERM_BG(0) through XTERM_BG(0). // const MUX_COLOR_SET aColors[] = { { 0, 0, "", 0, T(""), 0, T(""), 0}, // COLOR_NOTCOLOR { CS_NORMAL, CS_ALLBITS, ANSI_NORMAL, sizeof(ANSI_NORMAL)-1, T(COLOR_RESET), 3, T("%xn"), 3}, // COLOR_INDEX_RESET { CS_INTENSE, CS_INTENSE, ANSI_HILITE, sizeof(ANSI_HILITE)-1, T(COLOR_INTENSE), 3, T("%xh"), 3}, // COLOR_INDEX_ATTR, COLOR_INDEX_INTENSE { CS_UNDERLINE, CS_UNDERLINE, ANSI_UNDER, sizeof(ANSI_UNDER)-1, T(COLOR_UNDERLINE), 3, T("%xu"), 3}, // COLOR_INDEX_UNDERLINE { CS_BLINK, CS_BLINK, ANSI_BLINK, sizeof(ANSI_BLINK)-1, T(COLOR_BLINK), 3, T("%xf"), 3}, // COLOR_INDEX_BLINK { CS_INVERSE, CS_INVERSE, ANSI_INVERSE, sizeof(ANSI_INVERSE)-1, T(COLOR_INVERSE), 3, T("%xi"), 3}, // COLOR_INDEX_INVERSE { CS_FG_BLACK, CS_FOREGROUND, ANSI_BLACK, sizeof(ANSI_BLACK)-1, T(COLOR_FG_BLACK), 3, T("%xx"), 3}, // COLOR_INDEX_FG { CS_FG_RED, CS_FOREGROUND, ANSI_RED, sizeof(ANSI_RED)-1, T(COLOR_FG_RED), 3, T("%xr"), 3}, { CS_FG_GREEN, CS_FOREGROUND, ANSI_GREEN, sizeof(ANSI_GREEN)-1, T(COLOR_FG_GREEN), 3, T("%xg"), 3}, { CS_FG_YELLOW, CS_FOREGROUND, ANSI_YELLOW, sizeof(ANSI_YELLOW)-1, T(COLOR_FG_YELLOW), 3, T("%xy"), 3}, { CS_FG_BLUE, CS_FOREGROUND, ANSI_BLUE, sizeof(ANSI_BLUE)-1, T(COLOR_FG_BLUE), 3, T("%xb"), 3}, { CS_FG_MAGENTA, CS_FOREGROUND, ANSI_MAGENTA, sizeof(ANSI_MAGENTA)-1, T(COLOR_FG_MAGENTA), 3, T("%xm"), 3}, { CS_FG_CYAN, CS_FOREGROUND, ANSI_CYAN, sizeof(ANSI_CYAN)-1, T(COLOR_FG_CYAN), 3, T("%xc"), 3}, { CS_FG_WHITE, CS_FOREGROUND, ANSI_WHITE, sizeof(ANSI_WHITE)-1, T(COLOR_FG_WHITE), 3, T("%xw"), 3}, // COLOR_INDEX_FG_WHITE { CS_FG( 8), CS_FOREGROUND, XTERM_FG( 8), sizeof(XTERM_FG( 8))-1, T(COLOR_FG_555555), 3, T("NU8"), 6}, // These eight are converted into something else. { CS_FG( 9), CS_FOREGROUND, XTERM_FG( 9), sizeof(XTERM_FG( 9))-1, T(COLOR_FG_FF5555), 3, T("NU9"), 6}, // . { CS_FG( 10), CS_FOREGROUND, XTERM_FG( 10), sizeof(XTERM_FG( 10))-1, T(COLOR_FG_55FF55), 3, T("NU10"), 6}, // . { CS_FG( 11), CS_FOREGROUND, XTERM_FG( 11), sizeof(XTERM_FG( 11))-1, T(COLOR_FG_FFFF55), 3, T("NU11"), 6}, // . { CS_FG( 12), CS_FOREGROUND, XTERM_FG( 12), sizeof(XTERM_FG( 12))-1, T(COLOR_FG_5555FF), 3, T("NU12"), 6}, // . { CS_FG( 13), CS_FOREGROUND, XTERM_FG( 13), sizeof(XTERM_FG( 13))-1, T(COLOR_FG_FF55FF), 3, T("NU13"), 6}, // . { CS_FG( 14), CS_FOREGROUND, XTERM_FG( 14), sizeof(XTERM_FG( 14))-1, T(COLOR_FG_55FFFF), 3, T("NU14"), 6}, // . { CS_FG( 15), CS_FOREGROUND, XTERM_FG( 15), sizeof(XTERM_FG( 15))-1, T(COLOR_FG_FFFFFF_1),3, T("NU15"), 6}, // - { CS_FG( 16), CS_FOREGROUND, XTERM_FG( 16), sizeof(XTERM_FG( 16))-1, T(COLOR_FG_000000), 3, T("%x<#000000>"), 11}, { CS_FG( 17), CS_FOREGROUND, XTERM_FG( 17), sizeof(XTERM_FG( 17))-1, T(COLOR_FG_00005F), 3, T("%x<#00005F>"), 11}, { CS_FG( 18), CS_FOREGROUND, XTERM_FG( 18), sizeof(XTERM_FG( 18))-1, T(COLOR_FG_000087), 3, T("%x<#000087>"), 11}, { CS_FG( 19), CS_FOREGROUND, XTERM_FG( 19), sizeof(XTERM_FG( 19))-1, T(COLOR_FG_0000AF), 3, T("%x<#0000AF>"), 11}, { CS_FG( 20), CS_FOREGROUND, XTERM_FG( 20), sizeof(XTERM_FG( 20))-1, T(COLOR_FG_0000D7), 3, T("%x<#0000D7>"), 11}, { CS_FG( 21), CS_FOREGROUND, XTERM_FG( 21), sizeof(XTERM_FG( 21))-1, T(COLOR_FG_0000FF), 3, T("%x<#0000FF>"), 11}, { CS_FG( 22), CS_FOREGROUND, XTERM_FG( 22), sizeof(XTERM_FG( 22))-1, T(COLOR_FG_005F00), 3, T("%x<#005F00>"), 11}, { CS_FG( 23), CS_FOREGROUND, XTERM_FG( 23), sizeof(XTERM_FG( 23))-1, T(COLOR_FG_005F5F), 3, T("%x<#005F5F>"), 11}, { CS_FG( 24), CS_FOREGROUND, XTERM_FG( 24), sizeof(XTERM_FG( 24))-1, T(COLOR_FG_005F87), 3, T("%x<#005F87>"), 11}, { CS_FG( 25), CS_FOREGROUND, XTERM_FG( 25), sizeof(XTERM_FG( 25))-1, T(COLOR_FG_005FAF), 3, T("%x<#005FAF>"), 11}, { CS_FG( 26), CS_FOREGROUND, XTERM_FG( 26), sizeof(XTERM_FG( 26))-1, T(COLOR_FG_005FD7), 3, T("%x<#005FD7>"), 11}, { CS_FG( 27), CS_FOREGROUND, XTERM_FG( 27), sizeof(XTERM_FG( 27))-1, T(COLOR_FG_005FFF), 3, T("%x<#005FFF>"), 11}, { CS_FG( 28), CS_FOREGROUND, XTERM_FG( 28), sizeof(XTERM_FG( 28))-1, T(COLOR_FG_008700), 3, T("%x<#008700>"), 11}, { CS_FG( 29), CS_FOREGROUND, XTERM_FG( 29), sizeof(XTERM_FG( 29))-1, T(COLOR_FG_00875F), 3, T("%x<#00875F>"), 11}, { CS_FG( 30), CS_FOREGROUND, XTERM_FG( 30), sizeof(XTERM_FG( 30))-1, T(COLOR_FG_008785), 3, T("%x<#008785>"), 11}, { CS_FG( 31), CS_FOREGROUND, XTERM_FG( 31), sizeof(XTERM_FG( 31))-1, T(COLOR_FG_0087AF), 3, T("%x<#0087AF>"), 11}, { CS_FG( 32), CS_FOREGROUND, XTERM_FG( 32), sizeof(XTERM_FG( 32))-1, T(COLOR_FG_0087D7), 3, T("%x<#0087D7>"), 11}, { CS_FG( 33), CS_FOREGROUND, XTERM_FG( 33), sizeof(XTERM_FG( 33))-1, T(COLOR_FG_0087FF), 3, T("%x<#0087FF>"), 11}, { CS_FG( 34), CS_FOREGROUND, XTERM_FG( 34), sizeof(XTERM_FG( 34))-1, T(COLOR_FG_00AF00), 3, T("%x<#00AF00>"), 11}, { CS_FG( 35), CS_FOREGROUND, XTERM_FG( 35), sizeof(XTERM_FG( 35))-1, T(COLOR_FG_00AF5F), 3, T("%x<#00AF5F>"), 11}, { CS_FG( 36), CS_FOREGROUND, XTERM_FG( 36), sizeof(XTERM_FG( 36))-1, T(COLOR_FG_00AF87), 3, T("%x<#00AF87>"), 11}, { CS_FG( 37), CS_FOREGROUND, XTERM_FG( 37), sizeof(XTERM_FG( 37))-1, T(COLOR_FG_00AFAF), 3, T("%x<#00AFAF>"), 11}, { CS_FG( 38), CS_FOREGROUND, XTERM_FG( 38), sizeof(XTERM_FG( 38))-1, T(COLOR_FG_00AFD7), 3, T("%x<#00AFD7>"), 11}, { CS_FG( 39), CS_FOREGROUND, XTERM_FG( 39), sizeof(XTERM_FG( 39))-1, T(COLOR_FG_00AFFF), 3, T("%x<#00AFFF>"), 11}, { CS_FG( 40), CS_FOREGROUND, XTERM_FG( 40), sizeof(XTERM_FG( 40))-1, T(COLOR_FG_00D700), 3, T("%x<#00D700>"), 11}, { CS_FG( 41), CS_FOREGROUND, XTERM_FG( 41), sizeof(XTERM_FG( 41))-1, T(COLOR_FG_00D75F), 3, T("%x<#00D75F>"), 11}, { CS_FG( 42), CS_FOREGROUND, XTERM_FG( 42), sizeof(XTERM_FG( 42))-1, T(COLOR_FG_00D787), 3, T("%x<#00D787>"), 11}, { CS_FG( 43), CS_FOREGROUND, XTERM_FG( 43), sizeof(XTERM_FG( 43))-1, T(COLOR_FG_00D7AF), 3, T("%x<#00D7AF>"), 11}, { CS_FG( 44), CS_FOREGROUND, XTERM_FG( 44), sizeof(XTERM_FG( 44))-1, T(COLOR_FG_00D7D7), 3, T("%x<#00D7D7>"), 11}, { CS_FG( 45), CS_FOREGROUND, XTERM_FG( 45), sizeof(XTERM_FG( 45))-1, T(COLOR_FG_00D7FF), 3, T("%x<#00D7FF>"), 11}, { CS_FG( 46), CS_FOREGROUND, XTERM_FG( 46), sizeof(XTERM_FG( 46))-1, T(COLOR_FG_00FF00), 3, T("%x<#00FF00>"), 11}, { CS_FG( 47), CS_FOREGROUND, XTERM_FG( 47), sizeof(XTERM_FG( 47))-1, T(COLOR_FG_00FF5A), 3, T("%x<#00FF5A>"), 11}, { CS_FG( 48), CS_FOREGROUND, XTERM_FG( 48), sizeof(XTERM_FG( 48))-1, T(COLOR_FG_00FF87), 3, T("%x<#00FF87>"), 11}, { CS_FG( 49), CS_FOREGROUND, XTERM_FG( 49), sizeof(XTERM_FG( 49))-1, T(COLOR_FG_00FFAF), 3, T("%x<#00FFAF>"), 11}, { CS_FG( 50), CS_FOREGROUND, XTERM_FG( 50), sizeof(XTERM_FG( 50))-1, T(COLOR_FG_00FFD7), 3, T("%x<#00FFD7>"), 11}, { CS_FG( 51), CS_FOREGROUND, XTERM_FG( 51), sizeof(XTERM_FG( 51))-1, T(COLOR_FG_00FFFF), 3, T("%x<#00FFFF>"), 11}, { CS_FG( 52), CS_FOREGROUND, XTERM_FG( 52), sizeof(XTERM_FG( 52))-1, T(COLOR_FG_5F0000), 3, T("%x<#5F0000>"), 11}, { CS_FG( 53), CS_FOREGROUND, XTERM_FG( 53), sizeof(XTERM_FG( 53))-1, T(COLOR_FG_5F005F), 3, T("%x<#5F005F>"), 11}, { CS_FG( 54), CS_FOREGROUND, XTERM_FG( 54), sizeof(XTERM_FG( 54))-1, T(COLOR_FG_5F0087), 3, T("%x<#5F0087>"), 11}, { CS_FG( 55), CS_FOREGROUND, XTERM_FG( 55), sizeof(XTERM_FG( 55))-1, T(COLOR_FG_5F00AF), 3, T("%x<#5F00AF>"), 11}, { CS_FG( 56), CS_FOREGROUND, XTERM_FG( 56), sizeof(XTERM_FG( 56))-1, T(COLOR_FG_5F00D7), 3, T("%x<#5F00D7>"), 11}, { CS_FG( 57), CS_FOREGROUND, XTERM_FG( 57), sizeof(XTERM_FG( 57))-1, T(COLOR_FG_5F00FF), 3, T("%x<#5F00FF>"), 11}, { CS_FG( 58), CS_FOREGROUND, XTERM_FG( 58), sizeof(XTERM_FG( 58))-1, T(COLOR_FG_5F5F00), 3, T("%x<#5F5F00>"), 11}, { CS_FG( 59), CS_FOREGROUND, XTERM_FG( 59), sizeof(XTERM_FG( 59))-1, T(COLOR_FG_5F5F5F), 3, T("%x<#5F5F5F>"), 11}, { CS_FG( 60), CS_FOREGROUND, XTERM_FG( 60), sizeof(XTERM_FG( 60))-1, T(COLOR_FG_5F5F87), 3, T("%x<#5F5F87>"), 11}, { CS_FG( 61), CS_FOREGROUND, XTERM_FG( 61), sizeof(XTERM_FG( 61))-1, T(COLOR_FG_5F5FAF), 3, T("%x<#5F5FAF>"), 11}, { CS_FG( 62), CS_FOREGROUND, XTERM_FG( 62), sizeof(XTERM_FG( 62))-1, T(COLOR_FG_5F5FD7), 3, T("%x<#5F5FD7>"), 11}, { CS_FG( 63), CS_FOREGROUND, XTERM_FG( 63), sizeof(XTERM_FG( 63))-1, T(COLOR_FG_5F5FFF), 3, T("%x<#5F5FFF>"), 11}, { CS_FG( 64), CS_FOREGROUND, XTERM_FG( 64), sizeof(XTERM_FG( 64))-1, T(COLOR_FG_5F8700), 3, T("%x<#5F8700>"), 11}, { CS_FG( 65), CS_FOREGROUND, XTERM_FG( 65), sizeof(XTERM_FG( 65))-1, T(COLOR_FG_5F875F), 3, T("%x<#5F875F>"), 11}, { CS_FG( 66), CS_FOREGROUND, XTERM_FG( 66), sizeof(XTERM_FG( 66))-1, T(COLOR_FG_5F8787), 3, T("%x<#5F8787>"), 11}, { CS_FG( 67), CS_FOREGROUND, XTERM_FG( 67), sizeof(XTERM_FG( 67))-1, T(COLOR_FG_5F87AF), 3, T("%x<#5F87AF>"), 11}, { CS_FG( 68), CS_FOREGROUND, XTERM_FG( 68), sizeof(XTERM_FG( 68))-1, T(COLOR_FG_5F87D7), 3, T("%x<#5F87D7>"), 11}, { CS_FG( 69), CS_FOREGROUND, XTERM_FG( 69), sizeof(XTERM_FG( 69))-1, T(COLOR_FG_5F87FF), 3, T("%x<#5F87FF>"), 11}, { CS_FG( 70), CS_FOREGROUND, XTERM_FG( 70), sizeof(XTERM_FG( 70))-1, T(COLOR_FG_5FAF00), 3, T("%x<#5FAF00>"), 11}, { CS_FG( 71), CS_FOREGROUND, XTERM_FG( 71), sizeof(XTERM_FG( 71))-1, T(COLOR_FG_5FAF5F), 3, T("%x<#5FAF5F>"), 11}, { CS_FG( 72), CS_FOREGROUND, XTERM_FG( 72), sizeof(XTERM_FG( 72))-1, T(COLOR_FG_5FAF87), 3, T("%x<#5FAF87>"), 11}, { CS_FG( 73), CS_FOREGROUND, XTERM_FG( 73), sizeof(XTERM_FG( 73))-1, T(COLOR_FG_5FAFAF), 3, T("%x<#5FAFAF>"), 11}, { CS_FG( 74), CS_FOREGROUND, XTERM_FG( 74), sizeof(XTERM_FG( 74))-1, T(COLOR_FG_5FAFD7), 3, T("%x<#5FAFD7>"), 11}, { CS_FG( 75), CS_FOREGROUND, XTERM_FG( 75), sizeof(XTERM_FG( 75))-1, T(COLOR_FG_5FAFFF), 3, T("%x<#5FAFFF>"), 11}, { CS_FG( 76), CS_FOREGROUND, XTERM_FG( 76), sizeof(XTERM_FG( 76))-1, T(COLOR_FG_5FD700), 3, T("%x<#5FD700>"), 11}, { CS_FG( 77), CS_FOREGROUND, XTERM_FG( 77), sizeof(XTERM_FG( 77))-1, T(COLOR_FG_5FD75F), 3, T("%x<#5FD75F>"), 11}, { CS_FG( 78), CS_FOREGROUND, XTERM_FG( 78), sizeof(XTERM_FG( 78))-1, T(COLOR_FG_5FD787), 3, T("%x<#5FD787>"), 11}, { CS_FG( 79), CS_FOREGROUND, XTERM_FG( 79), sizeof(XTERM_FG( 79))-1, T(COLOR_FG_5FD7AF), 3, T("%x<#5FD7AF>"), 11}, { CS_FG( 80), CS_FOREGROUND, XTERM_FG( 80), sizeof(XTERM_FG( 80))-1, T(COLOR_FG_5FD7D7), 3, T("%x<#5FD7D7>"), 11}, { CS_FG( 81), CS_FOREGROUND, XTERM_FG( 81), sizeof(XTERM_FG( 81))-1, T(COLOR_FG_5FD7FF), 3, T("%x<#5FD7FF>"), 11}, { CS_FG( 82), CS_FOREGROUND, XTERM_FG( 82), sizeof(XTERM_FG( 82))-1, T(COLOR_FG_5FFF00), 3, T("%x<#5FFF00>"), 11}, { CS_FG( 83), CS_FOREGROUND, XTERM_FG( 83), sizeof(XTERM_FG( 83))-1, T(COLOR_FG_5FFF5F), 3, T("%x<#5FFF5F>"), 11}, { CS_FG( 84), CS_FOREGROUND, XTERM_FG( 84), sizeof(XTERM_FG( 84))-1, T(COLOR_FG_5FFF87), 3, T("%x<#5FFF87>"), 11}, { CS_FG( 85), CS_FOREGROUND, XTERM_FG( 85), sizeof(XTERM_FG( 85))-1, T(COLOR_FG_5FFFAF), 3, T("%x<#5FFFAF>"), 11}, { CS_FG( 86), CS_FOREGROUND, XTERM_FG( 86), sizeof(XTERM_FG( 86))-1, T(COLOR_FG_5FFFD7), 3, T("%x<#5FFFD7>"), 11}, { CS_FG( 87), CS_FOREGROUND, XTERM_FG( 87), sizeof(XTERM_FG( 87))-1, T(COLOR_FG_5FFFFF), 3, T("%x<#5FFFFF>"), 11}, { CS_FG( 88), CS_FOREGROUND, XTERM_FG( 88), sizeof(XTERM_FG( 88))-1, T(COLOR_FG_870000), 3, T("%x<#870000>"), 11}, { CS_FG( 89), CS_FOREGROUND, XTERM_FG( 89), sizeof(XTERM_FG( 89))-1, T(COLOR_FG_87005F), 3, T("%x<#87005F>"), 11}, { CS_FG( 90), CS_FOREGROUND, XTERM_FG( 90), sizeof(XTERM_FG( 90))-1, T(COLOR_FG_870087), 3, T("%x<#870087>"), 11}, { CS_FG( 91), CS_FOREGROUND, XTERM_FG( 91), sizeof(XTERM_FG( 91))-1, T(COLOR_FG_8700AF), 3, T("%x<#8700AF>"), 11}, { CS_FG( 92), CS_FOREGROUND, XTERM_FG( 92), sizeof(XTERM_FG( 92))-1, T(COLOR_FG_8700D7), 3, T("%x<#8700D7>"), 11}, { CS_FG( 93), CS_FOREGROUND, XTERM_FG( 93), sizeof(XTERM_FG( 93))-1, T(COLOR_FG_8700FF), 3, T("%x<#8700FF>"), 11}, { CS_FG( 94), CS_FOREGROUND, XTERM_FG( 94), sizeof(XTERM_FG( 94))-1, T(COLOR_FG_875F00), 3, T("%x<#875F00>"), 11}, { CS_FG( 95), CS_FOREGROUND, XTERM_FG( 95), sizeof(XTERM_FG( 95))-1, T(COLOR_FG_875F5F), 3, T("%x<#875F5F>"), 11}, { CS_FG( 96), CS_FOREGROUND, XTERM_FG( 96), sizeof(XTERM_FG( 96))-1, T(COLOR_FG_875F87), 3, T("%x<#875F87>"), 11}, { CS_FG( 97), CS_FOREGROUND, XTERM_FG( 97), sizeof(XTERM_FG( 97))-1, T(COLOR_FG_875FAF), 3, T("%x<#875FAF>"), 11}, { CS_FG( 98), CS_FOREGROUND, XTERM_FG( 98), sizeof(XTERM_FG( 98))-1, T(COLOR_FG_875FD7), 3, T("%x<#875FD7>"), 11}, { CS_FG( 99), CS_FOREGROUND, XTERM_FG( 99), sizeof(XTERM_FG( 99))-1, T(COLOR_FG_875FFF), 3, T("%x<#875FFF>"), 11}, { CS_FG(100), CS_FOREGROUND, XTERM_FG(100), sizeof(XTERM_FG(100))-1, T(COLOR_FG_878700), 3, T("%x<#878700>"), 11}, { CS_FG(101), CS_FOREGROUND, XTERM_FG(101), sizeof(XTERM_FG(101))-1, T(COLOR_FG_87875F), 3, T("%x<#87875F>"), 11}, { CS_FG(102), CS_FOREGROUND, XTERM_FG(102), sizeof(XTERM_FG(102))-1, T(COLOR_FG_878787), 3, T("%x<#878787>"), 11}, { CS_FG(103), CS_FOREGROUND, XTERM_FG(103), sizeof(XTERM_FG(103))-1, T(COLOR_FG_8787AF), 3, T("%x<#8787AF>"), 11}, { CS_FG(104), CS_FOREGROUND, XTERM_FG(104), sizeof(XTERM_FG(104))-1, T(COLOR_FG_8787D7), 3, T("%x<#8787D7>"), 11}, { CS_FG(105), CS_FOREGROUND, XTERM_FG(105), sizeof(XTERM_FG(105))-1, T(COLOR_FG_8787FF), 3, T("%x<#8787FF>"), 11}, { CS_FG(106), CS_FOREGROUND, XTERM_FG(106), sizeof(XTERM_FG(106))-1, T(COLOR_FG_87AF00), 3, T("%x<#87AF00>"), 11}, { CS_FG(107), CS_FOREGROUND, XTERM_FG(107), sizeof(XTERM_FG(107))-1, T(COLOR_FG_87AF5F), 3, T("%x<#87AF5F>"), 11}, { CS_FG(108), CS_FOREGROUND, XTERM_FG(108), sizeof(XTERM_FG(108))-1, T(COLOR_FG_87AF87), 3, T("%x<#87AF87>"), 11}, { CS_FG(109), CS_FOREGROUND, XTERM_FG(109), sizeof(XTERM_FG(109))-1, T(COLOR_FG_87AFAF), 3, T("%x<#87AFAF>"), 11}, { CS_FG(110), CS_FOREGROUND, XTERM_FG(110), sizeof(XTERM_FG(110))-1, T(COLOR_FG_87AFD7), 3, T("%x<#87AFD7>"), 11}, { CS_FG(111), CS_FOREGROUND, XTERM_FG(111), sizeof(XTERM_FG(111))-1, T(COLOR_FG_87AFFF), 3, T("%x<#87AFFF>"), 11}, { CS_FG(112), CS_FOREGROUND, XTERM_FG(112), sizeof(XTERM_FG(112))-1, T(COLOR_FG_87D700), 3, T("%x<#87D700>"), 11}, { CS_FG(113), CS_FOREGROUND, XTERM_FG(113), sizeof(XTERM_FG(113))-1, T(COLOR_FG_87D75A), 3, T("%x<#87D75A>"), 11}, { CS_FG(114), CS_FOREGROUND, XTERM_FG(114), sizeof(XTERM_FG(114))-1, T(COLOR_FG_87D787), 3, T("%x<#87D787>"), 11}, { CS_FG(115), CS_FOREGROUND, XTERM_FG(115), sizeof(XTERM_FG(115))-1, T(COLOR_FG_87D7AF), 3, T("%x<#87D7AF>"), 11}, { CS_FG(116), CS_FOREGROUND, XTERM_FG(116), sizeof(XTERM_FG(116))-1, T(COLOR_FG_87D7D7), 3, T("%x<#87D7D7>"), 11}, { CS_FG(117), CS_FOREGROUND, XTERM_FG(117), sizeof(XTERM_FG(117))-1, T(COLOR_FG_87D7FF), 3, T("%x<#87D7FF>"), 11}, { CS_FG(118), CS_FOREGROUND, XTERM_FG(118), sizeof(XTERM_FG(118))-1, T(COLOR_FG_87FF00), 3, T("%x<#87FF00>"), 11}, { CS_FG(119), CS_FOREGROUND, XTERM_FG(119), sizeof(XTERM_FG(119))-1, T(COLOR_FG_87FF5F), 3, T("%x<#87FF5F>"), 11}, { CS_FG(120), CS_FOREGROUND, XTERM_FG(120), sizeof(XTERM_FG(120))-1, T(COLOR_FG_87FF87), 3, T("%x<#87FF87>"), 11}, { CS_FG(121), CS_FOREGROUND, XTERM_FG(121), sizeof(XTERM_FG(121))-1, T(COLOR_FG_87FFAF), 3, T("%x<#87FFAF>"), 11}, { CS_FG(122), CS_FOREGROUND, XTERM_FG(122), sizeof(XTERM_FG(122))-1, T(COLOR_FG_87FFD7), 3, T("%x<#87FFD7>"), 11}, { CS_FG(123), CS_FOREGROUND, XTERM_FG(123), sizeof(XTERM_FG(123))-1, T(COLOR_FG_87FFFF), 3, T("%x<#87FFFF>"), 11}, { CS_FG(124), CS_FOREGROUND, XTERM_FG(124), sizeof(XTERM_FG(124))-1, T(COLOR_FG_AF0000), 3, T("%x<#AF0000>"), 11}, { CS_FG(125), CS_FOREGROUND, XTERM_FG(125), sizeof(XTERM_FG(125))-1, T(COLOR_FG_AF005F), 3, T("%x<#AF005F>"), 11}, { CS_FG(126), CS_FOREGROUND, XTERM_FG(126), sizeof(XTERM_FG(126))-1, T(COLOR_FG_AF0087), 3, T("%x<#AF0087>"), 11}, { CS_FG(127), CS_FOREGROUND, XTERM_FG(127), sizeof(XTERM_FG(127))-1, T(COLOR_FG_AF00AF), 3, T("%x<#AF00AF>"), 11}, { CS_FG(128), CS_FOREGROUND, XTERM_FG(128), sizeof(XTERM_FG(128))-1, T(COLOR_FG_AF00D7), 3, T("%x<#AF00D7>"), 11}, { CS_FG(129), CS_FOREGROUND, XTERM_FG(129), sizeof(XTERM_FG(129))-1, T(COLOR_FG_AF00FF), 3, T("%x<#AF00FF>"), 11}, { CS_FG(130), CS_FOREGROUND, XTERM_FG(130), sizeof(XTERM_FG(130))-1, T(COLOR_FG_AF5F00), 3, T("%x<#AF5F00>"), 11}, { CS_FG(131), CS_FOREGROUND, XTERM_FG(131), sizeof(XTERM_FG(131))-1, T(COLOR_FG_AF5F5F), 3, T("%x<#AF5F5F>"), 11}, { CS_FG(132), CS_FOREGROUND, XTERM_FG(132), sizeof(XTERM_FG(132))-1, T(COLOR_FG_AF5F87), 3, T("%x<#AF5F87>"), 11}, { CS_FG(133), CS_FOREGROUND, XTERM_FG(133), sizeof(XTERM_FG(133))-1, T(COLOR_FG_AF5FAF), 3, T("%x<#AF5FAF>"), 11}, { CS_FG(134), CS_FOREGROUND, XTERM_FG(134), sizeof(XTERM_FG(134))-1, T(COLOR_FG_AF5FD7), 3, T("%x<#AF5FD7>"), 11}, { CS_FG(135), CS_FOREGROUND, XTERM_FG(135), sizeof(XTERM_FG(135))-1, T(COLOR_FG_AF5FFF), 3, T("%x<#AF5FFF>"), 11}, { CS_FG(136), CS_FOREGROUND, XTERM_FG(136), sizeof(XTERM_FG(136))-1, T(COLOR_FG_AF8700), 3, T("%x<#AF8700>"), 11}, { CS_FG(137), CS_FOREGROUND, XTERM_FG(137), sizeof(XTERM_FG(137))-1, T(COLOR_FG_AF875F), 3, T("%x<#AF875F>"), 11}, { CS_FG(138), CS_FOREGROUND, XTERM_FG(138), sizeof(XTERM_FG(138))-1, T(COLOR_FG_AF8787), 3, T("%x<#AF8787>"), 11}, { CS_FG(139), CS_FOREGROUND, XTERM_FG(139), sizeof(XTERM_FG(139))-1, T(COLOR_FG_AF87AF), 3, T("%x<#AF87AF>"), 11}, { CS_FG(140), CS_FOREGROUND, XTERM_FG(140), sizeof(XTERM_FG(140))-1, T(COLOR_FG_AF87D7), 3, T("%x<#AF87D7>"), 11}, { CS_FG(141), CS_FOREGROUND, XTERM_FG(141), sizeof(XTERM_FG(141))-1, T(COLOR_FG_AF87FF), 3, T("%x<#AF87FF>"), 11}, { CS_FG(142), CS_FOREGROUND, XTERM_FG(142), sizeof(XTERM_FG(142))-1, T(COLOR_FG_AFAF00), 3, T("%x<#AFAF00>"), 11}, { CS_FG(143), CS_FOREGROUND, XTERM_FG(143), sizeof(XTERM_FG(143))-1, T(COLOR_FG_AFAF5F), 3, T("%x<#AFAF5F>"), 11}, { CS_FG(144), CS_FOREGROUND, XTERM_FG(144), sizeof(XTERM_FG(144))-1, T(COLOR_FG_AFAF87), 3, T("%x<#AFAF87>"), 11}, { CS_FG(145), CS_FOREGROUND, XTERM_FG(145), sizeof(XTERM_FG(145))-1, T(COLOR_FG_AFAFAF), 3, T("%x<#AFAFAF>"), 11}, { CS_FG(146), CS_FOREGROUND, XTERM_FG(146), sizeof(XTERM_FG(146))-1, T(COLOR_FG_AFAFD7), 3, T("%x<#AFAFD7>"), 11}, { CS_FG(147), CS_FOREGROUND, XTERM_FG(147), sizeof(XTERM_FG(147))-1, T(COLOR_FG_AFAFFF), 3, T("%x<#AFAFFF>"), 11}, { CS_FG(148), CS_FOREGROUND, XTERM_FG(148), sizeof(XTERM_FG(148))-1, T(COLOR_FG_AFD700), 3, T("%x<#AFD700>"), 11}, { CS_FG(149), CS_FOREGROUND, XTERM_FG(149), sizeof(XTERM_FG(149))-1, T(COLOR_FG_AFD75F), 3, T("%x<#AFD75F>"), 11}, { CS_FG(150), CS_FOREGROUND, XTERM_FG(150), sizeof(XTERM_FG(150))-1, T(COLOR_FG_AFD787), 3, T("%x<#AFD787>"), 11}, { CS_FG(151), CS_FOREGROUND, XTERM_FG(151), sizeof(XTERM_FG(151))-1, T(COLOR_FG_AFD7AF), 3, T("%x<#AFD7AF>"), 11}, { CS_FG(152), CS_FOREGROUND, XTERM_FG(152), sizeof(XTERM_FG(152))-1, T(COLOR_FG_AFD7D7), 3, T("%x<#AFD7D7>"), 11}, { CS_FG(153), CS_FOREGROUND, XTERM_FG(153), sizeof(XTERM_FG(153))-1, T(COLOR_FG_AFD7FF), 3, T("%x<#AFD7FF>"), 11}, { CS_FG(154), CS_FOREGROUND, XTERM_FG(154), sizeof(XTERM_FG(154))-1, T(COLOR_FG_AFFF00), 3, T("%x<#AFFF00>"), 11}, { CS_FG(155), CS_FOREGROUND, XTERM_FG(155), sizeof(XTERM_FG(155))-1, T(COLOR_FG_AFFF5F), 3, T("%x<#AFFF5F>"), 11}, { CS_FG(156), CS_FOREGROUND, XTERM_FG(156), sizeof(XTERM_FG(156))-1, T(COLOR_FG_AFFF87), 3, T("%x<#AFFF87>"), 11}, { CS_FG(157), CS_FOREGROUND, XTERM_FG(157), sizeof(XTERM_FG(157))-1, T(COLOR_FG_AFFFAF), 3, T("%x<#AFFFAF>"), 11}, { CS_FG(158), CS_FOREGROUND, XTERM_FG(158), sizeof(XTERM_FG(158))-1, T(COLOR_FG_AFFFD7), 3, T("%x<#AFFFD7>"), 11}, { CS_FG(159), CS_FOREGROUND, XTERM_FG(159), sizeof(XTERM_FG(159))-1, T(COLOR_FG_AFFFFF), 3, T("%x<#AFFFFF>"), 11}, { CS_FG(160), CS_FOREGROUND, XTERM_FG(160), sizeof(XTERM_FG(160))-1, T(COLOR_FG_D70000), 3, T("%x<#D70000>"), 11}, { CS_FG(161), CS_FOREGROUND, XTERM_FG(161), sizeof(XTERM_FG(161))-1, T(COLOR_FG_D7005F), 3, T("%x<#D7005F>"), 11}, { CS_FG(162), CS_FOREGROUND, XTERM_FG(162), sizeof(XTERM_FG(162))-1, T(COLOR_FG_D70087), 3, T("%x<#D70087>"), 11}, { CS_FG(163), CS_FOREGROUND, XTERM_FG(163), sizeof(XTERM_FG(163))-1, T(COLOR_FG_D700AF), 3, T("%x<#D700AF>"), 11}, { CS_FG(164), CS_FOREGROUND, XTERM_FG(164), sizeof(XTERM_FG(164))-1, T(COLOR_FG_D700D7), 3, T("%x<#D700D7>"), 11}, { CS_FG(165), CS_FOREGROUND, XTERM_FG(165), sizeof(XTERM_FG(165))-1, T(COLOR_FG_D700FF), 3, T("%x<#D700FF>"), 11}, { CS_FG(166), CS_FOREGROUND, XTERM_FG(166), sizeof(XTERM_FG(166))-1, T(COLOR_FG_D75F00), 3, T("%x<#D75F00>"), 11}, { CS_FG(167), CS_FOREGROUND, XTERM_FG(167), sizeof(XTERM_FG(167))-1, T(COLOR_FG_D75F5F), 3, T("%x<#D75F5F>"), 11}, { CS_FG(168), CS_FOREGROUND, XTERM_FG(168), sizeof(XTERM_FG(168))-1, T(COLOR_FG_D75F87), 3, T("%x<#D75F87>"), 11}, { CS_FG(169), CS_FOREGROUND, XTERM_FG(169), sizeof(XTERM_FG(169))-1, T(COLOR_FG_D75FAF), 3, T("%x<#D75FAF>"), 11}, { CS_FG(170), CS_FOREGROUND, XTERM_FG(170), sizeof(XTERM_FG(170))-1, T(COLOR_FG_D75FD7), 3, T("%x<#D75FD7>"), 11}, { CS_FG(171), CS_FOREGROUND, XTERM_FG(171), sizeof(XTERM_FG(171))-1, T(COLOR_FG_D75FFF), 3, T("%x<#D75FFF>"), 11}, { CS_FG(172), CS_FOREGROUND, XTERM_FG(172), sizeof(XTERM_FG(172))-1, T(COLOR_FG_D78700), 3, T("%x<#D78700>"), 11}, { CS_FG(173), CS_FOREGROUND, XTERM_FG(173), sizeof(XTERM_FG(173))-1, T(COLOR_FG_D7875A), 3, T("%x<#D7875A>"), 11}, { CS_FG(174), CS_FOREGROUND, XTERM_FG(174), sizeof(XTERM_FG(174))-1, T(COLOR_FG_D78787), 3, T("%x<#D78787>"), 11}, { CS_FG(175), CS_FOREGROUND, XTERM_FG(175), sizeof(XTERM_FG(175))-1, T(COLOR_FG_D787AF), 3, T("%x<#D787AF>"), 11}, { CS_FG(176), CS_FOREGROUND, XTERM_FG(176), sizeof(XTERM_FG(176))-1, T(COLOR_FG_D787D7), 3, T("%x<#D787D7>"), 11}, { CS_FG(177), CS_FOREGROUND, XTERM_FG(177), sizeof(XTERM_FG(177))-1, T(COLOR_FG_D787FF), 3, T("%x<#D787FF>"), 11}, { CS_FG(178), CS_FOREGROUND, XTERM_FG(178), sizeof(XTERM_FG(178))-1, T(COLOR_FG_D7AF00), 3, T("%x<#D7AF00>"), 11}, { CS_FG(179), CS_FOREGROUND, XTERM_FG(179), sizeof(XTERM_FG(179))-1, T(COLOR_FG_D7AF5A), 3, T("%x<#D7AF5A>"), 11}, { CS_FG(180), CS_FOREGROUND, XTERM_FG(180), sizeof(XTERM_FG(180))-1, T(COLOR_FG_D7AF87), 3, T("%x<#D7AF87>"), 11}, { CS_FG(181), CS_FOREGROUND, XTERM_FG(181), sizeof(XTERM_FG(181))-1, T(COLOR_FG_D7AFAF), 3, T("%x<#D7AFAF>"), 11}, { CS_FG(182), CS_FOREGROUND, XTERM_FG(182), sizeof(XTERM_FG(182))-1, T(COLOR_FG_D7AFD7), 3, T("%x<#D7AFD7>"), 11}, { CS_FG(183), CS_FOREGROUND, XTERM_FG(183), sizeof(XTERM_FG(183))-1, T(COLOR_FG_D7AFFF), 3, T("%x<#D7AFFF>"), 11}, { CS_FG(184), CS_FOREGROUND, XTERM_FG(184), sizeof(XTERM_FG(184))-1, T(COLOR_FG_D7D700), 3, T("%x<#D7D700>"), 11}, { CS_FG(185), CS_FOREGROUND, XTERM_FG(185), sizeof(XTERM_FG(185))-1, T(COLOR_FG_D7D75F), 3, T("%x<#D7D75F>"), 11}, { CS_FG(186), CS_FOREGROUND, XTERM_FG(186), sizeof(XTERM_FG(186))-1, T(COLOR_FG_D7D787), 3, T("%x<#D7D787>"), 11}, { CS_FG(187), CS_FOREGROUND, XTERM_FG(187), sizeof(XTERM_FG(187))-1, T(COLOR_FG_D7D7AF), 3, T("%x<#D7D7AF>"), 11}, { CS_FG(188), CS_FOREGROUND, XTERM_FG(188), sizeof(XTERM_FG(188))-1, T(COLOR_FG_D7D7D7), 3, T("%x<#D7D7D7>"), 11}, { CS_FG(189), CS_FOREGROUND, XTERM_FG(189), sizeof(XTERM_FG(189))-1, T(COLOR_FG_D7D7FF), 3, T("%x<#D7D7FF>"), 11}, { CS_FG(190), CS_FOREGROUND, XTERM_FG(190), sizeof(XTERM_FG(190))-1, T(COLOR_FG_D7FF00), 3, T("%x<#D7FF00>"), 11}, { CS_FG(191), CS_FOREGROUND, XTERM_FG(191), sizeof(XTERM_FG(191))-1, T(COLOR_FG_D7FF5F), 3, T("%x<#D7FF5F>"), 11}, { CS_FG(192), CS_FOREGROUND, XTERM_FG(192), sizeof(XTERM_FG(192))-1, T(COLOR_FG_D7FF87), 3, T("%x<#D7FF87>"), 11}, { CS_FG(193), CS_FOREGROUND, XTERM_FG(193), sizeof(XTERM_FG(193))-1, T(COLOR_FG_D7FFAF), 3, T("%x<#D7FFAF>"), 11}, { CS_FG(194), CS_FOREGROUND, XTERM_FG(194), sizeof(XTERM_FG(194))-1, T(COLOR_FG_D7FFD7), 3, T("%x<#D7FFD7>"), 11}, { CS_FG(195), CS_FOREGROUND, XTERM_FG(195), sizeof(XTERM_FG(195))-1, T(COLOR_FG_D7FFFF), 3, T("%x<#D7FFFF>"), 11}, { CS_FG(196), CS_FOREGROUND, XTERM_FG(196), sizeof(XTERM_FG(196))-1, T(COLOR_FG_FF0000), 3, T("%x<#FF0000>"), 11}, { CS_FG(197), CS_FOREGROUND, XTERM_FG(197), sizeof(XTERM_FG(197))-1, T(COLOR_FG_FF005F), 3, T("%x<#FF005F>"), 11}, { CS_FG(198), CS_FOREGROUND, XTERM_FG(198), sizeof(XTERM_FG(198))-1, T(COLOR_FG_FF0087), 3, T("%x<#FF0087>"), 11}, { CS_FG(199), CS_FOREGROUND, XTERM_FG(199), sizeof(XTERM_FG(199))-1, T(COLOR_FG_FF00AF), 3, T("%x<#FF00AF>"), 11}, { CS_FG(200), CS_FOREGROUND, XTERM_FG(200), sizeof(XTERM_FG(200))-1, T(COLOR_FG_FF00D7), 3, T("%x<#FF00D7>"), 11}, { CS_FG(201), CS_FOREGROUND, XTERM_FG(201), sizeof(XTERM_FG(201))-1, T(COLOR_FG_FF00FF), 3, T("%x<#FF00FF>"), 11}, { CS_FG(202), CS_FOREGROUND, XTERM_FG(202), sizeof(XTERM_FG(202))-1, T(COLOR_FG_FF5F00), 3, T("%x<#FF5F00>"), 11}, { CS_FG(203), CS_FOREGROUND, XTERM_FG(203), sizeof(XTERM_FG(203))-1, T(COLOR_FG_FF5F5F), 3, T("%x<#FF5F5F>"), 11}, { CS_FG(204), CS_FOREGROUND, XTERM_FG(204), sizeof(XTERM_FG(204))-1, T(COLOR_FG_FF5F87), 3, T("%x<#FF5F87>"), 11}, { CS_FG(205), CS_FOREGROUND, XTERM_FG(205), sizeof(XTERM_FG(205))-1, T(COLOR_FG_FF5FAF), 3, T("%x<#FF5FAF>"), 11}, { CS_FG(206), CS_FOREGROUND, XTERM_FG(206), sizeof(XTERM_FG(206))-1, T(COLOR_FG_FF5FD7), 3, T("%x<#FF5FD7>"), 11}, { CS_FG(207), CS_FOREGROUND, XTERM_FG(207), sizeof(XTERM_FG(207))-1, T(COLOR_FG_FF5FFF), 3, T("%x<#FF5FFF>"), 11}, { CS_FG(208), CS_FOREGROUND, XTERM_FG(208), sizeof(XTERM_FG(208))-1, T(COLOR_FG_FF8700), 3, T("%x<#FF8700>"), 11}, { CS_FG(209), CS_FOREGROUND, XTERM_FG(209), sizeof(XTERM_FG(209))-1, T(COLOR_FG_FF875F), 3, T("%x<#FF875F>"), 11}, { CS_FG(210), CS_FOREGROUND, XTERM_FG(210), sizeof(XTERM_FG(210))-1, T(COLOR_FG_FF8787), 3, T("%x<#FF8787>"), 11}, { CS_FG(211), CS_FOREGROUND, XTERM_FG(211), sizeof(XTERM_FG(211))-1, T(COLOR_FG_FF87AF), 3, T("%x<#FF87AF>"), 11}, { CS_FG(212), CS_FOREGROUND, XTERM_FG(212), sizeof(XTERM_FG(212))-1, T(COLOR_FG_FF87D7), 3, T("%x<#FF87D7>"), 11}, { CS_FG(213), CS_FOREGROUND, XTERM_FG(213), sizeof(XTERM_FG(213))-1, T(COLOR_FG_FF87FF), 3, T("%x<#FF87FF>"), 11}, { CS_FG(214), CS_FOREGROUND, XTERM_FG(214), sizeof(XTERM_FG(214))-1, T(COLOR_FG_FFAF00), 3, T("%x<#FFAF00>"), 11}, { CS_FG(215), CS_FOREGROUND, XTERM_FG(215), sizeof(XTERM_FG(215))-1, T(COLOR_FG_FFAF5F), 3, T("%x<#FFAF5F>"), 11}, { CS_FG(216), CS_FOREGROUND, XTERM_FG(216), sizeof(XTERM_FG(216))-1, T(COLOR_FG_FFAF87), 3, T("%x<#FFAF87>"), 11}, { CS_FG(217), CS_FOREGROUND, XTERM_FG(217), sizeof(XTERM_FG(217))-1, T(COLOR_FG_FFAFAF), 3, T("%x<#FFAFAF>"), 11}, { CS_FG(218), CS_FOREGROUND, XTERM_FG(218), sizeof(XTERM_FG(218))-1, T(COLOR_FG_FFAFD7), 3, T("%x<#FFAFD7>"), 11}, { CS_FG(219), CS_FOREGROUND, XTERM_FG(219), sizeof(XTERM_FG(219))-1, T(COLOR_FG_FFAFFF), 3, T("%x<#FFAFFF>"), 11}, { CS_FG(220), CS_FOREGROUND, XTERM_FG(220), sizeof(XTERM_FG(220))-1, T(COLOR_FG_FFD700), 3, T("%x<#FFD700>"), 11}, { CS_FG(221), CS_FOREGROUND, XTERM_FG(221), sizeof(XTERM_FG(221))-1, T(COLOR_FG_FFD75F), 3, T("%x<#FFD75F>"), 11}, { CS_FG(222), CS_FOREGROUND, XTERM_FG(222), sizeof(XTERM_FG(222))-1, T(COLOR_FG_FFD787), 3, T("%x<#FFD787>"), 11}, { CS_FG(223), CS_FOREGROUND, XTERM_FG(223), sizeof(XTERM_FG(223))-1, T(COLOR_FG_FFD7AF), 3, T("%x<#FFD7AF>"), 11}, { CS_FG(224), CS_FOREGROUND, XTERM_FG(224), sizeof(XTERM_FG(224))-1, T(COLOR_FG_FFD7D7), 3, T("%x<#FFD7D7>"), 11}, { CS_FG(225), CS_FOREGROUND, XTERM_FG(225), sizeof(XTERM_FG(225))-1, T(COLOR_FG_FFD7FF), 3, T("%x<#FFD7FF>"), 11}, { CS_FG(226), CS_FOREGROUND, XTERM_FG(226), sizeof(XTERM_FG(226))-1, T(COLOR_FG_FFFF00), 3, T("%x<#FFFF00>"), 11}, { CS_FG(227), CS_FOREGROUND, XTERM_FG(227), sizeof(XTERM_FG(227))-1, T(COLOR_FG_FFFF5F), 3, T("%x<#FFFF5F>"), 11}, { CS_FG(228), CS_FOREGROUND, XTERM_FG(228), sizeof(XTERM_FG(228))-1, T(COLOR_FG_FFFF87), 3, T("%x<#FFFF87>"), 11}, { CS_FG(229), CS_FOREGROUND, XTERM_FG(229), sizeof(XTERM_FG(229))-1, T(COLOR_FG_FFFFAF), 3, T("%x<#FFFFAF>"), 11}, { CS_FG(230), CS_FOREGROUND, XTERM_FG(230), sizeof(XTERM_FG(230))-1, T(COLOR_FG_FFFFD7), 3, T("%x<#FFFFD7>"), 11}, { CS_FG(231), CS_FOREGROUND, XTERM_FG(231), sizeof(XTERM_FG(231))-1, T(COLOR_FG_FFFFFF_2),3, T("%x<#FFFFFF>"), 11}, { CS_FG(232), CS_FOREGROUND, XTERM_FG(232), sizeof(XTERM_FG(232))-1, T(COLOR_FG_080808), 3, T("%x<#080808>"), 11}, { CS_FG(233), CS_FOREGROUND, XTERM_FG(233), sizeof(XTERM_FG(233))-1, T(COLOR_FG_121212), 3, T("%x<#121212>"), 11}, { CS_FG(234), CS_FOREGROUND, XTERM_FG(234), sizeof(XTERM_FG(234))-1, T(COLOR_FG_1C1C1C), 3, T("%x<#1C1C1C>"), 11}, { CS_FG(235), CS_FOREGROUND, XTERM_FG(235), sizeof(XTERM_FG(235))-1, T(COLOR_FG_262626), 3, T("%x<#262626>"), 11}, { CS_FG(236), CS_FOREGROUND, XTERM_FG(236), sizeof(XTERM_FG(236))-1, T(COLOR_FG_303030), 3, T("%x<#303030>"), 11}, { CS_FG(237), CS_FOREGROUND, XTERM_FG(237), sizeof(XTERM_FG(237))-1, T(COLOR_FG_3A3A3A), 3, T("%x<#3A3A3A>"), 11}, { CS_FG(238), CS_FOREGROUND, XTERM_FG(238), sizeof(XTERM_FG(238))-1, T(COLOR_FG_444444), 3, T("%x<#444444>"), 11}, { CS_FG(239), CS_FOREGROUND, XTERM_FG(239), sizeof(XTERM_FG(239))-1, T(COLOR_FG_4E4E4E), 3, T("%x<#4E4E4E>"), 11}, { CS_FG(240), CS_FOREGROUND, XTERM_FG(240), sizeof(XTERM_FG(240))-1, T(COLOR_FG_585858), 3, T("%x<#585858>"), 11}, { CS_FG(241), CS_FOREGROUND, XTERM_FG(241), sizeof(XTERM_FG(241))-1, T(COLOR_FG_626262), 3, T("%x<#626262>"), 11}, { CS_FG(242), CS_FOREGROUND, XTERM_FG(242), sizeof(XTERM_FG(242))-1, T(COLOR_FG_6C6C6C), 3, T("%x<#6C6C6C>"), 11}, { CS_FG(243), CS_FOREGROUND, XTERM_FG(243), sizeof(XTERM_FG(243))-1, T(COLOR_FG_767676), 3, T("%x<#767676>"), 11}, { CS_FG(244), CS_FOREGROUND, XTERM_FG(244), sizeof(XTERM_FG(244))-1, T(COLOR_FG_808080), 3, T("%x<#808080>"), 11}, { CS_FG(245), CS_FOREGROUND, XTERM_FG(245), sizeof(XTERM_FG(245))-1, T(COLOR_FG_8A8A8A), 3, T("%x<#8A8A8A>"), 11}, { CS_FG(246), CS_FOREGROUND, XTERM_FG(246), sizeof(XTERM_FG(246))-1, T(COLOR_FG_949494), 3, T("%x<#949494>"), 11}, { CS_FG(247), CS_FOREGROUND, XTERM_FG(247), sizeof(XTERM_FG(247))-1, T(COLOR_FG_9E9E9E), 3, T("%x<#9E9E9E>"), 11}, { CS_FG(248), CS_FOREGROUND, XTERM_FG(248), sizeof(XTERM_FG(248))-1, T(COLOR_FG_A8A8A8), 3, T("%x<#A8A8A8>"), 11}, { CS_FG(249), CS_FOREGROUND, XTERM_FG(249), sizeof(XTERM_FG(249))-1, T(COLOR_FG_B2B2B2), 3, T("%x<#B2B2B2>"), 11}, { CS_FG(250), CS_FOREGROUND, XTERM_FG(250), sizeof(XTERM_FG(250))-1, T(COLOR_FG_BCBCBC), 3, T("%x<#BCBCBC>"), 11}, { CS_FG(251), CS_FOREGROUND, XTERM_FG(251), sizeof(XTERM_FG(251))-1, T(COLOR_FG_C6C6C6), 3, T("%x<#C6C6C6>"), 11}, { CS_FG(252), CS_FOREGROUND, XTERM_FG(252), sizeof(XTERM_FG(252))-1, T(COLOR_FG_D0D0D0), 3, T("%x<#D0D0D0>"), 11}, { CS_FG(253), CS_FOREGROUND, XTERM_FG(253), sizeof(XTERM_FG(253))-1, T(COLOR_FG_DADADA), 3, T("%x<#DADADA>"), 11}, { CS_FG(254), CS_FOREGROUND, XTERM_FG(254), sizeof(XTERM_FG(254))-1, T(COLOR_FG_E4E4E4), 3, T("%x<#E4E4E4>"), 11}, { CS_FG(255), CS_FOREGROUND, XTERM_FG(255), sizeof(XTERM_FG(255))-1, T(COLOR_FG_EEEEEE), 3, T("%x<#EEEEEE>"), 11}, { CS_BG_BLACK, CS_BACKGROUND, ANSI_BBLACK, sizeof(ANSI_BBLACK)-1, T(COLOR_BG_BLACK), 3, T("%xX"), 3}, // COLOR_INDEX_BG { CS_BG_RED, CS_BACKGROUND, ANSI_BRED, sizeof(ANSI_BRED)-1, T(COLOR_BG_RED), 3, T("%xR"), 3}, { CS_BG_GREEN, CS_BACKGROUND, ANSI_BGREEN, sizeof(ANSI_BGREEN)-1, T(COLOR_BG_GREEN), 3, T("%xG"), 3}, { CS_BG_YELLOW, CS_BACKGROUND, ANSI_BYELLOW, sizeof(ANSI_BYELLOW)-1, T(COLOR_BG_YELLOW), 3, T("%xY"), 3}, { CS_BG_BLUE, CS_BACKGROUND, ANSI_BBLUE, sizeof(ANSI_BBLUE)-1, T(COLOR_BG_BLUE), 3, T("%xB"), 3}, { CS_BG_MAGENTA, CS_BACKGROUND, ANSI_BMAGENTA, sizeof(ANSI_BMAGENTA)-1, T(COLOR_BG_MAGENTA), 3, T("%xM"), 3}, { CS_BG_CYAN, CS_BACKGROUND, ANSI_BCYAN, sizeof(ANSI_BCYAN)-1, T(COLOR_BG_CYAN), 3, T("%xC"), 3}, { CS_BG_WHITE, CS_BACKGROUND, ANSI_BWHITE, sizeof(ANSI_BWHITE)-1, T(COLOR_BG_WHITE), 3, T("%xW"), 3}, { CS_BG( 8), CS_BACKGROUND, ANSI_BBLACK, sizeof(ANSI_BBLACK)-1, T(COLOR_BG_555555), 3, T("%xX"), 3}, // These eight are never used. { CS_BG( 9), CS_BACKGROUND, ANSI_BRED, sizeof(ANSI_BRED)-1, T(COLOR_BG_FF5555), 3, T("%xR"), 3}, // . { CS_BG( 10), CS_BACKGROUND, ANSI_BGREEN, sizeof(ANSI_BGREEN)-1, T(COLOR_BG_55FF55), 3, T("%xG"), 3}, // . { CS_BG( 11), CS_BACKGROUND, ANSI_BYELLOW, sizeof(ANSI_BYELLOW)-1, T(COLOR_BG_FFFF55), 3, T("%xY"), 3}, // . { CS_BG( 12), CS_BACKGROUND, ANSI_BBLUE, sizeof(ANSI_BBLUE)-1, T(COLOR_BG_5555FF), 3, T("%xB"), 3}, // . { CS_BG( 13), CS_BACKGROUND, ANSI_BMAGENTA, sizeof(ANSI_BMAGENTA)-1, T(COLOR_BG_FF55FF), 3, T("%xM"), 3}, // . { CS_BG( 14), CS_BACKGROUND, ANSI_BCYAN, sizeof(ANSI_BCYAN)-1, T(COLOR_BG_55FFFF), 3, T("%xC"), 3}, // . { CS_BG( 15), CS_BACKGROUND, ANSI_BWHITE, sizeof(ANSI_BWHITE)-1, T(COLOR_BG_FFFFFF_1),3, T("%xW"), 3}, // - { CS_BG( 16), CS_BACKGROUND, XTERM_BG( 16), sizeof(XTERM_BG( 16))-1, T(COLOR_BG_000000), 3, T("%X<#000000>"), 11}, { CS_BG( 17), CS_BACKGROUND, XTERM_BG( 17), sizeof(XTERM_BG( 17))-1, T(COLOR_BG_00005F), 3, T("%X<#00005F>"), 11}, { CS_BG( 18), CS_BACKGROUND, XTERM_BG( 18), sizeof(XTERM_BG( 18))-1, T(COLOR_BG_000087), 3, T("%X<#000087>"), 11}, { CS_BG( 19), CS_BACKGROUND, XTERM_BG( 19), sizeof(XTERM_BG( 19))-1, T(COLOR_BG_0000AF), 3, T("%X<#0000AF>"), 11}, { CS_BG( 20), CS_BACKGROUND, XTERM_BG( 20), sizeof(XTERM_BG( 20))-1, T(COLOR_BG_0000D7), 3, T("%X<#0000D7>"), 11}, { CS_BG( 21), CS_BACKGROUND, XTERM_BG( 21), sizeof(XTERM_BG( 21))-1, T(COLOR_BG_0000FF), 3, T("%X<#0000FF>"), 11}, { CS_BG( 22), CS_BACKGROUND, XTERM_BG( 22), sizeof(XTERM_BG( 22))-1, T(COLOR_BG_005F00), 3, T("%X<#005F00>"), 11}, { CS_BG( 23), CS_BACKGROUND, XTERM_BG( 23), sizeof(XTERM_BG( 23))-1, T(COLOR_BG_005F5F), 3, T("%X<#005F5F>"), 11}, { CS_BG( 24), CS_BACKGROUND, XTERM_BG( 24), sizeof(XTERM_BG( 24))-1, T(COLOR_BG_005F87), 3, T("%X<#005F87>"), 11}, { CS_BG( 25), CS_BACKGROUND, XTERM_BG( 25), sizeof(XTERM_BG( 25))-1, T(COLOR_BG_005FAF), 3, T("%X<#005FAF>"), 11}, { CS_BG( 26), CS_BACKGROUND, XTERM_BG( 26), sizeof(XTERM_BG( 26))-1, T(COLOR_BG_005FD7), 3, T("%X<#005FD7>"), 11}, { CS_BG( 27), CS_BACKGROUND, XTERM_BG( 27), sizeof(XTERM_BG( 27))-1, T(COLOR_BG_005FFF), 3, T("%X<#005FFF>"), 11}, { CS_BG( 28), CS_BACKGROUND, XTERM_BG( 28), sizeof(XTERM_BG( 28))-1, T(COLOR_BG_008700), 3, T("%X<#008700>"), 11}, { CS_BG( 29), CS_BACKGROUND, XTERM_BG( 29), sizeof(XTERM_BG( 29))-1, T(COLOR_BG_00875F), 3, T("%X<#00875F>"), 11}, { CS_BG( 30), CS_BACKGROUND, XTERM_BG( 30), sizeof(XTERM_BG( 30))-1, T(COLOR_BG_008785), 3, T("%X<#008785>"), 11}, { CS_BG( 31), CS_BACKGROUND, XTERM_BG( 31), sizeof(XTERM_BG( 31))-1, T(COLOR_BG_0087AF), 3, T("%X<#0087AF>"), 11}, { CS_BG( 32), CS_BACKGROUND, XTERM_BG( 32), sizeof(XTERM_BG( 32))-1, T(COLOR_BG_0087D7), 3, T("%X<#0087D7>"), 11}, { CS_BG( 33), CS_BACKGROUND, XTERM_BG( 33), sizeof(XTERM_BG( 33))-1, T(COLOR_BG_0087FF), 3, T("%X<#0087FF>"), 11}, { CS_BG( 34), CS_BACKGROUND, XTERM_BG( 34), sizeof(XTERM_BG( 34))-1, T(COLOR_BG_00AF00), 3, T("%X<#00AF00>"), 11}, { CS_BG( 35), CS_BACKGROUND, XTERM_BG( 35), sizeof(XTERM_BG( 35))-1, T(COLOR_BG_00AF5F), 3, T("%X<#00AF5F>"), 11}, { CS_BG( 36), CS_BACKGROUND, XTERM_BG( 36), sizeof(XTERM_BG( 36))-1, T(COLOR_BG_00AF87), 3, T("%X<#00AF87>"), 11}, { CS_BG( 37), CS_BACKGROUND, XTERM_BG( 37), sizeof(XTERM_BG( 37))-1, T(COLOR_BG_00AFAF), 3, T("%X<#00AFAF>"), 11}, { CS_BG( 38), CS_BACKGROUND, XTERM_BG( 38), sizeof(XTERM_BG( 38))-1, T(COLOR_BG_00AFD7), 3, T("%X<#00AFD7>"), 11}, { CS_BG( 39), CS_BACKGROUND, XTERM_BG( 39), sizeof(XTERM_BG( 39))-1, T(COLOR_BG_00AFFF), 3, T("%X<#00AFFF>"), 11}, { CS_BG( 40), CS_BACKGROUND, XTERM_BG( 40), sizeof(XTERM_BG( 40))-1, T(COLOR_BG_00D700), 3, T("%X<#00D700>"), 11}, { CS_BG( 41), CS_BACKGROUND, XTERM_BG( 41), sizeof(XTERM_BG( 41))-1, T(COLOR_BG_00D75F), 3, T("%X<#00D75F>"), 11}, { CS_BG( 42), CS_BACKGROUND, XTERM_BG( 42), sizeof(XTERM_BG( 42))-1, T(COLOR_BG_00D787), 3, T("%X<#00D787>"), 11}, { CS_BG( 43), CS_BACKGROUND, XTERM_BG( 43), sizeof(XTERM_BG( 43))-1, T(COLOR_BG_00D7AF), 3, T("%X<#00D7AF>"), 11}, { CS_BG( 44), CS_BACKGROUND, XTERM_BG( 44), sizeof(XTERM_BG( 44))-1, T(COLOR_BG_00D7D7), 3, T("%X<#00D7D7>"), 11}, { CS_BG( 45), CS_BACKGROUND, XTERM_BG( 45), sizeof(XTERM_BG( 45))-1, T(COLOR_BG_00D7FF), 3, T("%X<#00D7FF>"), 11}, { CS_BG( 46), CS_BACKGROUND, XTERM_BG( 46), sizeof(XTERM_BG( 46))-1, T(COLOR_BG_00FF00), 3, T("%X<#00FF00>"), 11}, { CS_BG( 47), CS_BACKGROUND, XTERM_BG( 47), sizeof(XTERM_BG( 47))-1, T(COLOR_BG_00FF5A), 3, T("%X<#00FF5A>"), 11}, { CS_BG( 48), CS_BACKGROUND, XTERM_BG( 48), sizeof(XTERM_BG( 48))-1, T(COLOR_BG_00FF87), 3, T("%X<#00FF87>"), 11}, { CS_BG( 49), CS_BACKGROUND, XTERM_BG( 49), sizeof(XTERM_BG( 49))-1, T(COLOR_BG_00FFAF), 3, T("%X<#00FFAF>"), 11}, { CS_BG( 50), CS_BACKGROUND, XTERM_BG( 50), sizeof(XTERM_BG( 50))-1, T(COLOR_BG_00FFD7), 3, T("%X<#00FFD7>"), 11}, { CS_BG( 51), CS_BACKGROUND, XTERM_BG( 51), sizeof(XTERM_BG( 51))-1, T(COLOR_BG_00FFFF), 3, T("%X<#00FFFF>"), 11}, { CS_BG( 52), CS_BACKGROUND, XTERM_BG( 52), sizeof(XTERM_BG( 52))-1, T(COLOR_BG_5F0000), 3, T("%X<#5F0000>"), 11}, { CS_BG( 53), CS_BACKGROUND, XTERM_BG( 53), sizeof(XTERM_BG( 53))-1, T(COLOR_BG_5F005F), 3, T("%X<#5F005F>"), 11}, { CS_BG( 54), CS_BACKGROUND, XTERM_BG( 54), sizeof(XTERM_BG( 54))-1, T(COLOR_BG_5F0087), 3, T("%X<#5F0087>"), 11}, { CS_BG( 55), CS_BACKGROUND, XTERM_BG( 55), sizeof(XTERM_BG( 55))-1, T(COLOR_BG_5F00AF), 3, T("%X<#5F00AF>"), 11}, { CS_BG( 56), CS_BACKGROUND, XTERM_BG( 56), sizeof(XTERM_BG( 56))-1, T(COLOR_BG_5F00D7), 3, T("%X<#5F00D7>"), 11}, { CS_BG( 57), CS_BACKGROUND, XTERM_BG( 57), sizeof(XTERM_BG( 57))-1, T(COLOR_BG_5F00FF), 3, T("%X<#5F00FF>"), 11}, { CS_BG( 58), CS_BACKGROUND, XTERM_BG( 58), sizeof(XTERM_BG( 58))-1, T(COLOR_BG_5F5F00), 3, T("%X<#5F5F00>"), 11}, { CS_BG( 59), CS_BACKGROUND, XTERM_BG( 59), sizeof(XTERM_BG( 59))-1, T(COLOR_BG_5F5F5F), 3, T("%X<#5F5F5F>"), 11}, { CS_BG( 60), CS_BACKGROUND, XTERM_BG( 60), sizeof(XTERM_BG( 60))-1, T(COLOR_BG_5F5F87), 3, T("%X<#5F5F87>"), 11}, { CS_BG( 61), CS_BACKGROUND, XTERM_BG( 61), sizeof(XTERM_BG( 61))-1, T(COLOR_BG_5F5FAF), 3, T("%X<#5F5FAF>"), 11}, { CS_BG( 62), CS_BACKGROUND, XTERM_BG( 62), sizeof(XTERM_BG( 62))-1, T(COLOR_BG_5F5FD7), 3, T("%X<#5F5FD7>"), 11}, { CS_BG( 63), CS_BACKGROUND, XTERM_BG( 63), sizeof(XTERM_BG( 63))-1, T(COLOR_BG_5F5FFF), 3, T("%X<#5F5FFF>"), 11}, { CS_BG( 64), CS_BACKGROUND, XTERM_BG( 64), sizeof(XTERM_BG( 64))-1, T(COLOR_BG_5F8700), 3, T("%X<#5F8700>"), 11}, { CS_BG( 65), CS_BACKGROUND, XTERM_BG( 65), sizeof(XTERM_BG( 65))-1, T(COLOR_BG_5F875F), 3, T("%X<#5F875F>"), 11}, { CS_BG( 66), CS_BACKGROUND, XTERM_BG( 66), sizeof(XTERM_BG( 66))-1, T(COLOR_BG_5F8787), 3, T("%X<#5F8787>"), 11}, { CS_BG( 67), CS_BACKGROUND, XTERM_BG( 67), sizeof(XTERM_BG( 67))-1, T(COLOR_BG_5F87AF), 3, T("%X<#5F87AF>"), 11}, { CS_BG( 68), CS_BACKGROUND, XTERM_BG( 68), sizeof(XTERM_BG( 68))-1, T(COLOR_BG_5F87D7), 3, T("%X<#5F87D7>"), 11}, { CS_BG( 69), CS_BACKGROUND, XTERM_BG( 69), sizeof(XTERM_BG( 69))-1, T(COLOR_BG_5F87FF), 3, T("%X<#5F87FF>"), 11}, { CS_BG( 70), CS_BACKGROUND, XTERM_BG( 70), sizeof(XTERM_BG( 70))-1, T(COLOR_BG_5FAF00), 3, T("%X<#5FAF00>"), 11}, { CS_BG( 71), CS_BACKGROUND, XTERM_BG( 71), sizeof(XTERM_BG( 71))-1, T(COLOR_BG_5FAF5F), 3, T("%X<#5FAF5F>"), 11}, { CS_BG( 72), CS_BACKGROUND, XTERM_BG( 72), sizeof(XTERM_BG( 72))-1, T(COLOR_BG_5FAF87), 3, T("%X<#5FAF87>"), 11}, { CS_BG( 73), CS_BACKGROUND, XTERM_BG( 73), sizeof(XTERM_BG( 73))-1, T(COLOR_BG_5FAFAF), 3, T("%X<#5FAFAF>"), 11}, { CS_BG( 74), CS_BACKGROUND, XTERM_BG( 74), sizeof(XTERM_BG( 74))-1, T(COLOR_BG_5FAFD7), 3, T("%X<#5FAFD7>"), 11}, { CS_BG( 75), CS_BACKGROUND, XTERM_BG( 75), sizeof(XTERM_BG( 75))-1, T(COLOR_BG_5FAFFF), 3, T("%X<#5FAFFF>"), 11}, { CS_BG( 76), CS_BACKGROUND, XTERM_BG( 76), sizeof(XTERM_BG( 76))-1, T(COLOR_BG_5FD700), 3, T("%X<#5FD700>"), 11}, { CS_BG( 77), CS_BACKGROUND, XTERM_BG( 77), sizeof(XTERM_BG( 77))-1, T(COLOR_BG_5FD75F), 3, T("%X<#5FD75F>"), 11}, { CS_BG( 78), CS_BACKGROUND, XTERM_BG( 78), sizeof(XTERM_BG( 78))-1, T(COLOR_BG_5FD787), 3, T("%X<#5FD787>"), 11}, { CS_BG( 79), CS_BACKGROUND, XTERM_BG( 79), sizeof(XTERM_BG( 79))-1, T(COLOR_BG_5FD7AF), 3, T("%X<#5FD7AF>"), 11}, { CS_BG( 80), CS_BACKGROUND, XTERM_BG( 80), sizeof(XTERM_BG( 80))-1, T(COLOR_BG_5FD7D7), 3, T("%X<#5FD7D7>"), 11}, { CS_BG( 81), CS_BACKGROUND, XTERM_BG( 81), sizeof(XTERM_BG( 81))-1, T(COLOR_BG_5FD7FF), 3, T("%X<#5FD7FF>"), 11}, { CS_BG( 82), CS_BACKGROUND, XTERM_BG( 82), sizeof(XTERM_BG( 82))-1, T(COLOR_BG_5FFF00), 3, T("%X<#5FFF00>"), 11}, { CS_BG( 83), CS_BACKGROUND, XTERM_BG( 83), sizeof(XTERM_BG( 83))-1, T(COLOR_BG_5FFF5F), 3, T("%X<#5FFF5F>"), 11}, { CS_BG( 84), CS_BACKGROUND, XTERM_BG( 84), sizeof(XTERM_BG( 84))-1, T(COLOR_BG_5FFF87), 3, T("%X<#5FFF87>"), 11}, { CS_BG( 85), CS_BACKGROUND, XTERM_BG( 85), sizeof(XTERM_BG( 85))-1, T(COLOR_BG_5FFFAF), 3, T("%X<#5FFFAF>"), 11}, { CS_BG( 86), CS_BACKGROUND, XTERM_BG( 86), sizeof(XTERM_BG( 86))-1, T(COLOR_BG_5FFFD7), 3, T("%X<#5FFFD7>"), 11}, { CS_BG( 87), CS_BACKGROUND, XTERM_BG( 87), sizeof(XTERM_BG( 87))-1, T(COLOR_BG_5FFFFF), 3, T("%X<#5FFFFF>"), 11}, { CS_BG( 88), CS_BACKGROUND, XTERM_BG( 88), sizeof(XTERM_BG( 88))-1, T(COLOR_BG_870000), 3, T("%X<#870000>"), 11}, { CS_BG( 89), CS_BACKGROUND, XTERM_BG( 89), sizeof(XTERM_BG( 89))-1, T(COLOR_BG_87005F), 3, T("%X<#87005F>"), 11}, { CS_BG( 90), CS_BACKGROUND, XTERM_BG( 90), sizeof(XTERM_BG( 90))-1, T(COLOR_BG_870087), 3, T("%X<#870087>"), 11}, { CS_BG( 91), CS_BACKGROUND, XTERM_BG( 91), sizeof(XTERM_BG( 91))-1, T(COLOR_BG_8700AF), 3, T("%X<#8700AF>"), 11}, { CS_BG( 92), CS_BACKGROUND, XTERM_BG( 92), sizeof(XTERM_BG( 92))-1, T(COLOR_BG_8700D7), 3, T("%X<#8700D7>"), 11}, { CS_BG( 93), CS_BACKGROUND, XTERM_BG( 93), sizeof(XTERM_BG( 93))-1, T(COLOR_BG_8700FF), 3, T("%X<#8700FF>"), 11}, { CS_BG( 94), CS_BACKGROUND, XTERM_BG( 94), sizeof(XTERM_BG( 94))-1, T(COLOR_BG_875F00), 3, T("%X<#875F00>"), 11}, { CS_BG( 95), CS_BACKGROUND, XTERM_BG( 95), sizeof(XTERM_BG( 95))-1, T(COLOR_BG_875F5F), 3, T("%X<#875F5F>"), 11}, { CS_BG( 96), CS_BACKGROUND, XTERM_BG( 96), sizeof(XTERM_BG( 96))-1, T(COLOR_BG_875F87), 3, T("%X<#875F87>"), 11}, { CS_BG( 97), CS_BACKGROUND, XTERM_BG( 97), sizeof(XTERM_BG( 97))-1, T(COLOR_BG_875FAF), 3, T("%X<#875FAF>"), 11}, { CS_BG( 98), CS_BACKGROUND, XTERM_BG( 98), sizeof(XTERM_BG( 98))-1, T(COLOR_BG_875FD7), 3, T("%X<#875FD7>"), 11}, { CS_BG( 99), CS_BACKGROUND, XTERM_BG( 99), sizeof(XTERM_BG( 99))-1, T(COLOR_BG_875FFF), 3, T("%X<#875FFF>"), 11}, { CS_BG(100), CS_BACKGROUND, XTERM_BG(100), sizeof(XTERM_BG(100))-1, T(COLOR_BG_878700), 3, T("%X<#878700>"), 11}, { CS_BG(101), CS_BACKGROUND, XTERM_BG(101), sizeof(XTERM_BG(101))-1, T(COLOR_BG_87875F), 3, T("%X<#87875F>"), 11}, { CS_BG(102), CS_BACKGROUND, XTERM_BG(102), sizeof(XTERM_BG(102))-1, T(COLOR_BG_878787), 3, T("%X<#878787>"), 11}, { CS_BG(103), CS_BACKGROUND, XTERM_BG(103), sizeof(XTERM_BG(103))-1, T(COLOR_BG_8787AF), 3, T("%X<#8787AF>"), 11}, { CS_BG(104), CS_BACKGROUND, XTERM_BG(104), sizeof(XTERM_BG(104))-1, T(COLOR_BG_8787D7), 3, T("%X<#8787D7>"), 11}, { CS_BG(105), CS_BACKGROUND, XTERM_BG(105), sizeof(XTERM_BG(105))-1, T(COLOR_BG_8787FF), 3, T("%X<#8787FF>"), 11}, { CS_BG(106), CS_BACKGROUND, XTERM_BG(106), sizeof(XTERM_BG(106))-1, T(COLOR_BG_87AF00), 3, T("%X<#87AF00>"), 11}, { CS_BG(107), CS_BACKGROUND, XTERM_BG(107), sizeof(XTERM_BG(107))-1, T(COLOR_BG_87AF5F), 3, T("%X<#87AF5F>"), 11}, { CS_BG(108), CS_BACKGROUND, XTERM_BG(108), sizeof(XTERM_BG(108))-1, T(COLOR_BG_87AF87), 3, T("%X<#87AF87>"), 11}, { CS_BG(109), CS_BACKGROUND, XTERM_BG(109), sizeof(XTERM_BG(109))-1, T(COLOR_BG_87AFAF), 3, T("%X<#87AFAF>"), 11}, { CS_BG(110), CS_BACKGROUND, XTERM_BG(110), sizeof(XTERM_BG(110))-1, T(COLOR_BG_87AFD7), 3, T("%X<#87AFD7>"), 11}, { CS_BG(111), CS_BACKGROUND, XTERM_BG(111), sizeof(XTERM_BG(111))-1, T(COLOR_BG_87AFFF), 3, T("%X<#87AFFF>"), 11}, { CS_BG(112), CS_BACKGROUND, XTERM_BG(112), sizeof(XTERM_BG(112))-1, T(COLOR_BG_87D700), 3, T("%X<#87D700>"), 11}, { CS_BG(113), CS_BACKGROUND, XTERM_BG(113), sizeof(XTERM_BG(113))-1, T(COLOR_BG_87D75A), 3, T("%X<#87D75A>"), 11}, { CS_BG(114), CS_BACKGROUND, XTERM_BG(114), sizeof(XTERM_BG(114))-1, T(COLOR_BG_87D787), 3, T("%X<#87D787>"), 11}, { CS_BG(115), CS_BACKGROUND, XTERM_BG(115), sizeof(XTERM_BG(115))-1, T(COLOR_BG_87D7AF), 3, T("%X<#87D7AF>"), 11}, { CS_BG(116), CS_BACKGROUND, XTERM_BG(116), sizeof(XTERM_BG(116))-1, T(COLOR_BG_87D7D7), 3, T("%X<#87D7D7>"), 11}, { CS_BG(117), CS_BACKGROUND, XTERM_BG(117), sizeof(XTERM_BG(117))-1, T(COLOR_BG_87D7FF), 3, T("%X<#87D7FF>"), 11}, { CS_BG(118), CS_BACKGROUND, XTERM_BG(118), sizeof(XTERM_BG(118))-1, T(COLOR_BG_87FF00), 3, T("%X<#87FF00>"), 11}, { CS_BG(119), CS_BACKGROUND, XTERM_BG(119), sizeof(XTERM_BG(119))-1, T(COLOR_BG_87FF5F), 3, T("%X<#87FF5F>"), 11}, { CS_BG(120), CS_BACKGROUND, XTERM_BG(120), sizeof(XTERM_BG(120))-1, T(COLOR_BG_87FF87), 3, T("%X<#87FF87>"), 11}, { CS_BG(121), CS_BACKGROUND, XTERM_BG(121), sizeof(XTERM_BG(121))-1, T(COLOR_BG_87FFAF), 3, T("%X<#87FFAF>"), 11}, { CS_BG(122), CS_BACKGROUND, XTERM_BG(122), sizeof(XTERM_BG(122))-1, T(COLOR_BG_87FFD7), 3, T("%X<#87FFD7>"), 11}, { CS_BG(123), CS_BACKGROUND, XTERM_BG(123), sizeof(XTERM_BG(123))-1, T(COLOR_BG_87FFFF), 3, T("%X<#87FFFF>"), 11}, { CS_BG(124), CS_BACKGROUND, XTERM_BG(124), sizeof(XTERM_BG(124))-1, T(COLOR_BG_AF0000), 3, T("%X<#AF0000>"), 11}, { CS_BG(125), CS_BACKGROUND, XTERM_BG(125), sizeof(XTERM_BG(125))-1, T(COLOR_BG_AF005F), 3, T("%X<#AF005F>"), 11}, { CS_BG(126), CS_BACKGROUND, XTERM_BG(126), sizeof(XTERM_BG(126))-1, T(COLOR_BG_AF0087), 3, T("%X<#AF0087>"), 11}, { CS_BG(127), CS_BACKGROUND, XTERM_BG(127), sizeof(XTERM_BG(127))-1, T(COLOR_BG_AF00AF), 3, T("%X<#AF00AF>"), 11}, { CS_BG(128), CS_BACKGROUND, XTERM_BG(128), sizeof(XTERM_BG(128))-1, T(COLOR_BG_AF00D7), 3, T("%X<#AF00D7>"), 11}, { CS_BG(129), CS_BACKGROUND, XTERM_BG(129), sizeof(XTERM_BG(129))-1, T(COLOR_BG_AF00FF), 3, T("%X<#AF00FF>"), 11}, { CS_BG(130), CS_BACKGROUND, XTERM_BG(130), sizeof(XTERM_BG(130))-1, T(COLOR_BG_AF5F00), 3, T("%X<#AF5F00>"), 11}, { CS_BG(131), CS_BACKGROUND, XTERM_BG(131), sizeof(XTERM_BG(131))-1, T(COLOR_BG_AF5F5F), 3, T("%X<#AF5F5F>"), 11}, { CS_BG(132), CS_BACKGROUND, XTERM_BG(132), sizeof(XTERM_BG(132))-1, T(COLOR_BG_AF5F87), 3, T("%X<#AF5F87>"), 11}, { CS_BG(133), CS_BACKGROUND, XTERM_BG(133), sizeof(XTERM_BG(133))-1, T(COLOR_BG_AF5FAF), 3, T("%X<#AF5FAF>"), 11}, { CS_BG(134), CS_BACKGROUND, XTERM_BG(134), sizeof(XTERM_BG(134))-1, T(COLOR_BG_AF5FD7), 3, T("%X<#AF5FD7>"), 11}, { CS_BG(135), CS_BACKGROUND, XTERM_BG(135), sizeof(XTERM_BG(135))-1, T(COLOR_BG_AF5FFF), 3, T("%X<#AF5FFF>"), 11}, { CS_BG(136), CS_BACKGROUND, XTERM_BG(136), sizeof(XTERM_BG(136))-1, T(COLOR_BG_AF8700), 3, T("%X<#AF8700>"), 11}, { CS_BG(137), CS_BACKGROUND, XTERM_BG(137), sizeof(XTERM_BG(137))-1, T(COLOR_BG_AF875F), 3, T("%X<#AF875F>"), 11}, { CS_BG(138), CS_BACKGROUND, XTERM_BG(138), sizeof(XTERM_BG(138))-1, T(COLOR_BG_AF8787), 3, T("%X<#AF8787>"), 11}, { CS_BG(139), CS_BACKGROUND, XTERM_BG(139), sizeof(XTERM_BG(139))-1, T(COLOR_BG_AF87AF), 3, T("%X<#AF87AF>"), 11}, { CS_BG(140), CS_BACKGROUND, XTERM_BG(140), sizeof(XTERM_BG(140))-1, T(COLOR_BG_AF87D7), 3, T("%X<#AF87D7>"), 11}, { CS_BG(141), CS_BACKGROUND, XTERM_BG(141), sizeof(XTERM_BG(141))-1, T(COLOR_BG_AF87FF), 3, T("%X<#AF87FF>"), 11}, { CS_BG(142), CS_BACKGROUND, XTERM_BG(142), sizeof(XTERM_BG(142))-1, T(COLOR_BG_AFAF00), 3, T("%X<#AFAF00>"), 11}, { CS_BG(143), CS_BACKGROUND, XTERM_BG(143), sizeof(XTERM_BG(143))-1, T(COLOR_BG_AFAF5F), 3, T("%X<#AFAF5F>"), 11}, { CS_BG(144), CS_BACKGROUND, XTERM_BG(144), sizeof(XTERM_BG(144))-1, T(COLOR_BG_AFAF87), 3, T("%X<#AFAF87>"), 11}, { CS_BG(145), CS_BACKGROUND, XTERM_BG(145), sizeof(XTERM_BG(145))-1, T(COLOR_BG_AFAFAF), 3, T("%X<#AFAFAF>"), 11}, { CS_BG(146), CS_BACKGROUND, XTERM_BG(146), sizeof(XTERM_BG(146))-1, T(COLOR_BG_AFAFD7), 3, T("%X<#AFAFD7>"), 11}, { CS_BG(147), CS_BACKGROUND, XTERM_BG(147), sizeof(XTERM_BG(147))-1, T(COLOR_BG_AFAFFF), 3, T("%X<#AFAFFF>"), 11}, { CS_BG(148), CS_BACKGROUND, XTERM_BG(148), sizeof(XTERM_BG(148))-1, T(COLOR_BG_AFD700), 3, T("%X<#AFD700>"), 11}, { CS_BG(149), CS_BACKGROUND, XTERM_BG(149), sizeof(XTERM_BG(149))-1, T(COLOR_BG_AFD75F), 3, T("%X<#AFD75F>"), 11}, { CS_BG(150), CS_BACKGROUND, XTERM_BG(150), sizeof(XTERM_BG(150))-1, T(COLOR_BG_AFD787), 3, T("%X<#AFD787>"), 11}, { CS_BG(151), CS_BACKGROUND, XTERM_BG(151), sizeof(XTERM_BG(151))-1, T(COLOR_BG_AFD7AF), 3, T("%X<#AFD7AF>"), 11}, { CS_BG(152), CS_BACKGROUND, XTERM_BG(152), sizeof(XTERM_BG(152))-1, T(COLOR_BG_AFD7D7), 3, T("%X<#AFD7D7>"), 11}, { CS_BG(153), CS_BACKGROUND, XTERM_BG(153), sizeof(XTERM_BG(153))-1, T(COLOR_BG_AFD7FF), 3, T("%X<#AFD7FF>"), 11}, { CS_BG(154), CS_BACKGROUND, XTERM_BG(154), sizeof(XTERM_BG(154))-1, T(COLOR_BG_AFFF00), 3, T("%X<#AFFF00>"), 11}, { CS_BG(155), CS_BACKGROUND, XTERM_BG(155), sizeof(XTERM_BG(155))-1, T(COLOR_BG_AFFF5F), 3, T("%X<#AFFF5F>"), 11}, { CS_BG(156), CS_BACKGROUND, XTERM_BG(156), sizeof(XTERM_BG(156))-1, T(COLOR_BG_AFFF87), 3, T("%X<#AFFF87>"), 11}, { CS_BG(157), CS_BACKGROUND, XTERM_BG(157), sizeof(XTERM_BG(157))-1, T(COLOR_BG_AFFFAF), 3, T("%X<#AFFFAF>"), 11}, { CS_BG(158), CS_BACKGROUND, XTERM_BG(158), sizeof(XTERM_BG(158))-1, T(COLOR_BG_AFFFD7), 3, T("%X<#AFFFD7>"), 11}, { CS_BG(159), CS_BACKGROUND, XTERM_BG(159), sizeof(XTERM_BG(159))-1, T(COLOR_BG_AFFFFF), 3, T("%X<#AFFFFF>"), 11}, { CS_BG(160), CS_BACKGROUND, XTERM_BG(160), sizeof(XTERM_BG(160))-1, T(COLOR_BG_D70000), 3, T("%X<#D70000>"), 11}, { CS_BG(161), CS_BACKGROUND, XTERM_BG(161), sizeof(XTERM_BG(161))-1, T(COLOR_BG_D7005F), 3, T("%X<#D7005F>"), 11}, { CS_BG(162), CS_BACKGROUND, XTERM_BG(162), sizeof(XTERM_BG(162))-1, T(COLOR_BG_D70087), 3, T("%X<#D70087>"), 11}, { CS_BG(163), CS_BACKGROUND, XTERM_BG(163), sizeof(XTERM_BG(163))-1, T(COLOR_BG_D700AF), 3, T("%X<#D700AF>"), 11}, { CS_BG(164), CS_BACKGROUND, XTERM_BG(164), sizeof(XTERM_BG(164))-1, T(COLOR_BG_D700D7), 3, T("%X<#D700D7>"), 11}, { CS_BG(165), CS_BACKGROUND, XTERM_BG(165), sizeof(XTERM_BG(165))-1, T(COLOR_BG_D700FF), 3, T("%X<#D700FF>"), 11}, { CS_BG(166), CS_BACKGROUND, XTERM_BG(166), sizeof(XTERM_BG(166))-1, T(COLOR_BG_D75F00), 3, T("%X<#D75F00>"), 11}, { CS_BG(167), CS_BACKGROUND, XTERM_BG(167), sizeof(XTERM_BG(167))-1, T(COLOR_BG_D75F5F), 3, T("%X<#D75F5F>"), 11}, { CS_BG(168), CS_BACKGROUND, XTERM_BG(168), sizeof(XTERM_BG(168))-1, T(COLOR_BG_D75F87), 3, T("%X<#D75F87>"), 11}, { CS_BG(169), CS_BACKGROUND, XTERM_BG(169), sizeof(XTERM_BG(169))-1, T(COLOR_BG_D75FAF), 3, T("%X<#D75FAF>"), 11}, { CS_BG(170), CS_BACKGROUND, XTERM_BG(170), sizeof(XTERM_BG(170))-1, T(COLOR_BG_D75FD7), 3, T("%X<#D75FD7>"), 11}, { CS_BG(171), CS_BACKGROUND, XTERM_BG(171), sizeof(XTERM_BG(171))-1, T(COLOR_BG_D75FFF), 3, T("%X<#D75FFF>"), 11}, { CS_BG(172), CS_BACKGROUND, XTERM_BG(172), sizeof(XTERM_BG(172))-1, T(COLOR_BG_D78700), 3, T("%X<#D78700>"), 11}, { CS_BG(173), CS_BACKGROUND, XTERM_BG(173), sizeof(XTERM_BG(173))-1, T(COLOR_BG_D7875A), 3, T("%X<#D7875A>"), 11}, { CS_BG(174), CS_BACKGROUND, XTERM_BG(174), sizeof(XTERM_BG(174))-1, T(COLOR_BG_D78787), 3, T("%X<#D78787>"), 11}, { CS_BG(175), CS_BACKGROUND, XTERM_BG(175), sizeof(XTERM_BG(175))-1, T(COLOR_BG_D787AF), 3, T("%X<#D787AF>"), 11}, { CS_BG(176), CS_BACKGROUND, XTERM_BG(176), sizeof(XTERM_BG(176))-1, T(COLOR_BG_D787D7), 3, T("%X<#D787D7>"), 11}, { CS_BG(177), CS_BACKGROUND, XTERM_BG(177), sizeof(XTERM_BG(177))-1, T(COLOR_BG_D787FF), 3, T("%X<#D787FF>"), 11}, { CS_BG(178), CS_BACKGROUND, XTERM_BG(178), sizeof(XTERM_BG(178))-1, T(COLOR_BG_D7AF00), 3, T("%X<#D7AF00>"), 11}, { CS_BG(179), CS_BACKGROUND, XTERM_BG(179), sizeof(XTERM_BG(179))-1, T(COLOR_BG_D7AF5A), 3, T("%X<#D7AF5A>"), 11}, { CS_BG(180), CS_BACKGROUND, XTERM_BG(180), sizeof(XTERM_BG(180))-1, T(COLOR_BG_D7AF87), 3, T("%X<#D7AF87>"), 11}, { CS_BG(181), CS_BACKGROUND, XTERM_BG(181), sizeof(XTERM_BG(181))-1, T(COLOR_BG_D7AFAF), 3, T("%X<#D7AFAF>"), 11}, { CS_BG(182), CS_BACKGROUND, XTERM_BG(182), sizeof(XTERM_BG(182))-1, T(COLOR_BG_D7AFD7), 3, T("%X<#D7AFD7>"), 11}, { CS_BG(183), CS_BACKGROUND, XTERM_BG(183), sizeof(XTERM_BG(183))-1, T(COLOR_BG_D7AFFF), 3, T("%X<#D7AFFF>"), 11}, { CS_BG(184), CS_BACKGROUND, XTERM_BG(184), sizeof(XTERM_BG(184))-1, T(COLOR_BG_D7D700), 3, T("%X<#D7D700>"), 11}, { CS_BG(185), CS_BACKGROUND, XTERM_BG(185), sizeof(XTERM_BG(185))-1, T(COLOR_BG_D7D75F), 3, T("%X<#D7D75F>"), 11}, { CS_BG(186), CS_BACKGROUND, XTERM_BG(186), sizeof(XTERM_BG(186))-1, T(COLOR_BG_D7D787), 3, T("%X<#D7D787>"), 11}, { CS_BG(187), CS_BACKGROUND, XTERM_BG(187), sizeof(XTERM_BG(187))-1, T(COLOR_BG_D7D7AF), 3, T("%X<#D7D7AF>"), 11}, { CS_BG(188), CS_BACKGROUND, XTERM_BG(188), sizeof(XTERM_BG(188))-1, T(COLOR_BG_D7D7D7), 3, T("%X<#D7D7D7>"), 11}, { CS_BG(189), CS_BACKGROUND, XTERM_BG(189), sizeof(XTERM_BG(189))-1, T(COLOR_BG_D7D7FF), 3, T("%X<#D7D7FF>"), 11}, { CS_BG(190), CS_BACKGROUND, XTERM_BG(190), sizeof(XTERM_BG(190))-1, T(COLOR_BG_D7FF00), 3, T("%X<#D7FF00>"), 11}, { CS_BG(191), CS_BACKGROUND, XTERM_BG(191), sizeof(XTERM_BG(191))-1, T(COLOR_BG_D7FF5F), 3, T("%X<#D7FF5F>"), 11}, { CS_BG(192), CS_BACKGROUND, XTERM_BG(192), sizeof(XTERM_BG(192))-1, T(COLOR_BG_D7FF87), 3, T("%X<#D7FF87>"), 11}, { CS_BG(193), CS_BACKGROUND, XTERM_BG(193), sizeof(XTERM_BG(193))-1, T(COLOR_BG_D7FFAF), 3, T("%X<#D7FFAF>"), 11}, { CS_BG(194), CS_BACKGROUND, XTERM_BG(194), sizeof(XTERM_BG(194))-1, T(COLOR_BG_D7FFD7), 3, T("%X<#D7FFD7>"), 11}, { CS_BG(195), CS_BACKGROUND, XTERM_BG(195), sizeof(XTERM_BG(195))-1, T(COLOR_BG_D7FFFF), 3, T("%X<#D7FFFF>"), 11}, { CS_BG(196), CS_BACKGROUND, XTERM_BG(196), sizeof(XTERM_BG(196))-1, T(COLOR_BG_FF0000), 3, T("%X<#FF0000>"), 11}, { CS_BG(197), CS_BACKGROUND, XTERM_BG(197), sizeof(XTERM_BG(197))-1, T(COLOR_BG_FF005F), 3, T("%X<#FF005F>"), 11}, { CS_BG(198), CS_BACKGROUND, XTERM_BG(198), sizeof(XTERM_BG(198))-1, T(COLOR_BG_FF0087), 3, T("%X<#FF0087>"), 11}, { CS_BG(199), CS_BACKGROUND, XTERM_BG(199), sizeof(XTERM_BG(199))-1, T(COLOR_BG_FF00AF), 3, T("%X<#FF00AF>"), 11}, { CS_BG(200), CS_BACKGROUND, XTERM_BG(200), sizeof(XTERM_BG(200))-1, T(COLOR_BG_FF00D7), 3, T("%X<#FF00D7>"), 11}, { CS_BG(201), CS_BACKGROUND, XTERM_BG(201), sizeof(XTERM_BG(201))-1, T(COLOR_BG_FF00FF), 3, T("%X<#FF00FF>"), 11}, { CS_BG(202), CS_BACKGROUND, XTERM_BG(202), sizeof(XTERM_BG(202))-1, T(COLOR_BG_FF5F00), 3, T("%X<#FF5F00>"), 11}, { CS_BG(203), CS_BACKGROUND, XTERM_BG(203), sizeof(XTERM_BG(203))-1, T(COLOR_BG_FF5F5F), 3, T("%X<#FF5F5F>"), 11}, { CS_BG(204), CS_BACKGROUND, XTERM_BG(204), sizeof(XTERM_BG(204))-1, T(COLOR_BG_FF5F87), 3, T("%X<#FF5F87>"), 11}, { CS_BG(205), CS_BACKGROUND, XTERM_BG(205), sizeof(XTERM_BG(205))-1, T(COLOR_BG_FF5FAF), 3, T("%X<#FF5FAF>"), 11}, { CS_BG(206), CS_BACKGROUND, XTERM_BG(206), sizeof(XTERM_BG(206))-1, T(COLOR_BG_FF5FD7), 3, T("%X<#FF5FD7>"), 11}, { CS_BG(207), CS_BACKGROUND, XTERM_BG(207), sizeof(XTERM_BG(207))-1, T(COLOR_BG_FF5FFF), 3, T("%X<#FF5FFF>"), 11}, { CS_BG(208), CS_BACKGROUND, XTERM_BG(208), sizeof(XTERM_BG(208))-1, T(COLOR_BG_FF8700), 3, T("%X<#FF8700>"), 11}, { CS_BG(209), CS_BACKGROUND, XTERM_BG(209), sizeof(XTERM_BG(209))-1, T(COLOR_BG_FF875F), 3, T("%X<#FF875F>"), 11}, { CS_BG(210), CS_BACKGROUND, XTERM_BG(210), sizeof(XTERM_BG(210))-1, T(COLOR_BG_FF8787), 3, T("%X<#FF8787>"), 11}, { CS_BG(211), CS_BACKGROUND, XTERM_BG(211), sizeof(XTERM_BG(211))-1, T(COLOR_BG_FF87AF), 3, T("%X<#FF87AF>"), 11}, { CS_BG(212), CS_BACKGROUND, XTERM_BG(212), sizeof(XTERM_BG(212))-1, T(COLOR_BG_FF87D7), 3, T("%X<#FF87D7>"), 11}, { CS_BG(213), CS_BACKGROUND, XTERM_BG(213), sizeof(XTERM_BG(213))-1, T(COLOR_BG_FF87FF), 3, T("%X<#FF87FF>"), 11}, { CS_BG(214), CS_BACKGROUND, XTERM_BG(214), sizeof(XTERM_BG(214))-1, T(COLOR_BG_FFAF00), 3, T("%X<#FFAF00>"), 11}, { CS_BG(215), CS_BACKGROUND, XTERM_BG(215), sizeof(XTERM_BG(215))-1, T(COLOR_BG_FFAF5F), 3, T("%X<#FFAF5F>"), 11}, { CS_BG(216), CS_BACKGROUND, XTERM_BG(216), sizeof(XTERM_BG(216))-1, T(COLOR_BG_FFAF87), 3, T("%X<#FFAF87>"), 11}, { CS_BG(217), CS_BACKGROUND, XTERM_BG(217), sizeof(XTERM_BG(217))-1, T(COLOR_BG_FFAFAF), 3, T("%X<#FFAFAF>"), 11}, { CS_BG(218), CS_BACKGROUND, XTERM_BG(218), sizeof(XTERM_BG(218))-1, T(COLOR_BG_FFAFD7), 3, T("%X<#FFAFD7>"), 11}, { CS_BG(219), CS_BACKGROUND, XTERM_BG(219), sizeof(XTERM_BG(219))-1, T(COLOR_BG_FFAFFF), 3, T("%X<#FFAFFF>"), 11}, { CS_BG(220), CS_BACKGROUND, XTERM_BG(220), sizeof(XTERM_BG(220))-1, T(COLOR_BG_FFD700), 3, T("%X<#FFD700>"), 11}, { CS_BG(221), CS_BACKGROUND, XTERM_BG(221), sizeof(XTERM_BG(221))-1, T(COLOR_BG_FFD75F), 3, T("%X<#FFD75F>"), 11}, { CS_BG(222), CS_BACKGROUND, XTERM_BG(222), sizeof(XTERM_BG(222))-1, T(COLOR_BG_FFD787), 3, T("%X<#FFD787>"), 11}, { CS_BG(223), CS_BACKGROUND, XTERM_BG(223), sizeof(XTERM_BG(223))-1, T(COLOR_BG_FFD7AF), 3, T("%X<#FFD7AF>"), 11}, { CS_BG(224), CS_BACKGROUND, XTERM_BG(224), sizeof(XTERM_BG(224))-1, T(COLOR_BG_FFD7D7), 3, T("%X<#FFD7D7>"), 11}, { CS_BG(225), CS_BACKGROUND, XTERM_BG(225), sizeof(XTERM_BG(225))-1, T(COLOR_BG_FFD7FF), 3, T("%X<#FFD7FF>"), 11}, { CS_BG(226), CS_BACKGROUND, XTERM_BG(226), sizeof(XTERM_BG(226))-1, T(COLOR_BG_FFFF00), 3, T("%X<#FFFF00>"), 11}, { CS_BG(227), CS_BACKGROUND, XTERM_BG(227), sizeof(XTERM_BG(227))-1, T(COLOR_BG_FFFF5F), 3, T("%X<#FFFF5F>"), 11}, { CS_BG(228), CS_BACKGROUND, XTERM_BG(228), sizeof(XTERM_BG(228))-1, T(COLOR_BG_FFFF87), 3, T("%X<#FFFF87>"), 11}, { CS_BG(229), CS_BACKGROUND, XTERM_BG(229), sizeof(XTERM_BG(229))-1, T(COLOR_BG_FFFFAF), 3, T("%X<#FFFFAF>"), 11}, { CS_BG(230), CS_BACKGROUND, XTERM_BG(230), sizeof(XTERM_BG(230))-1, T(COLOR_BG_FFFFD7), 3, T("%X<#FFFFD7>"), 11}, { CS_BG(231), CS_BACKGROUND, XTERM_BG(231), sizeof(XTERM_BG(231))-1, T(COLOR_BG_FFFFFF_2),3, T("%X<#FFFFFF>"), 11}, { CS_BG(232), CS_BACKGROUND, XTERM_BG(232), sizeof(XTERM_BG(232))-1, T(COLOR_BG_080808), 3, T("%X<#080808>"), 11}, { CS_BG(233), CS_BACKGROUND, XTERM_BG(233), sizeof(XTERM_BG(233))-1, T(COLOR_BG_121212), 3, T("%X<#121212>"), 11}, { CS_BG(234), CS_BACKGROUND, XTERM_BG(234), sizeof(XTERM_BG(234))-1, T(COLOR_BG_1C1C1C), 3, T("%X<#1C1C1C>"), 11}, { CS_BG(235), CS_BACKGROUND, XTERM_BG(235), sizeof(XTERM_BG(235))-1, T(COLOR_BG_262626), 3, T("%X<#262626>"), 11}, { CS_BG(236), CS_BACKGROUND, XTERM_BG(236), sizeof(XTERM_BG(236))-1, T(COLOR_BG_303030), 3, T("%X<#303030>"), 11}, { CS_BG(237), CS_BACKGROUND, XTERM_BG(237), sizeof(XTERM_BG(237))-1, T(COLOR_BG_3A3A3A), 3, T("%X<#3A3A3A>"), 11}, { CS_BG(238), CS_BACKGROUND, XTERM_BG(238), sizeof(XTERM_BG(238))-1, T(COLOR_BG_444444), 3, T("%X<#444444>"), 11}, { CS_BG(239), CS_BACKGROUND, XTERM_BG(239), sizeof(XTERM_BG(239))-1, T(COLOR_BG_4E4E4E), 3, T("%X<#4E4E4E>"), 11}, { CS_BG(240), CS_BACKGROUND, XTERM_BG(240), sizeof(XTERM_BG(240))-1, T(COLOR_BG_585858), 3, T("%X<#585858>"), 11}, { CS_BG(241), CS_BACKGROUND, XTERM_BG(241), sizeof(XTERM_BG(241))-1, T(COLOR_BG_626262), 3, T("%X<#626262>"), 11}, { CS_BG(242), CS_BACKGROUND, XTERM_BG(242), sizeof(XTERM_BG(242))-1, T(COLOR_BG_6C6C6C), 3, T("%X<#6C6C6C>"), 11}, { CS_BG(243), CS_BACKGROUND, XTERM_BG(243), sizeof(XTERM_BG(243))-1, T(COLOR_BG_767676), 3, T("%X<#767676>"), 11}, { CS_BG(244), CS_BACKGROUND, XTERM_BG(244), sizeof(XTERM_BG(244))-1, T(COLOR_BG_808080), 3, T("%X<#808080>"), 11}, { CS_BG(245), CS_BACKGROUND, XTERM_BG(245), sizeof(XTERM_BG(245))-1, T(COLOR_BG_8A8A8A), 3, T("%X<#8A8A8A>"), 11}, { CS_BG(246), CS_BACKGROUND, XTERM_BG(246), sizeof(XTERM_BG(246))-1, T(COLOR_BG_949494), 3, T("%X<#949494>"), 11}, { CS_BG(247), CS_BACKGROUND, XTERM_BG(247), sizeof(XTERM_BG(247))-1, T(COLOR_BG_9E9E9E), 3, T("%X<#9E9E9E>"), 11}, { CS_BG(248), CS_BACKGROUND, XTERM_BG(248), sizeof(XTERM_BG(248))-1, T(COLOR_BG_A8A8A8), 3, T("%X<#A8A8A8>"), 11}, { CS_BG(249), CS_BACKGROUND, XTERM_BG(249), sizeof(XTERM_BG(249))-1, T(COLOR_BG_B2B2B2), 3, T("%X<#B2B2B2>"), 11}, { CS_BG(250), CS_BACKGROUND, XTERM_BG(250), sizeof(XTERM_BG(250))-1, T(COLOR_BG_BCBCBC), 3, T("%X<#BCBCBC>"), 11}, { CS_BG(251), CS_BACKGROUND, XTERM_BG(251), sizeof(XTERM_BG(251))-1, T(COLOR_BG_C6C6C6), 3, T("%X<#C6C6C6>"), 11}, { CS_BG(252), CS_BACKGROUND, XTERM_BG(252), sizeof(XTERM_BG(252))-1, T(COLOR_BG_D0D0D0), 3, T("%X<#D0D0D0>"), 11}, { CS_BG(253), CS_BACKGROUND, XTERM_BG(253), sizeof(XTERM_BG(253))-1, T(COLOR_BG_DADADA), 3, T("%X<#DADADA>"), 11}, { CS_BG(254), CS_BACKGROUND, XTERM_BG(254), sizeof(XTERM_BG(254))-1, T(COLOR_BG_E4E4E4), 3, T("%X<#E4E4E4>"), 11}, { CS_BG(255), CS_BACKGROUND, XTERM_BG(255), sizeof(XTERM_BG(255))-1, T(COLOR_BG_EEEEEE), 3, T("%X<#EEEEEE>"), 11}, }; /*! \brief Validate ColorState. * * Checks (with assertions) that the given ColorState is valid. This is * useful during development and debugging, but if any of the assertions are * false, the process ends. * * \param cs ColorState. * \return None. */ inline void ValidateColorState(ColorState cs) { const ColorState Mask = static_cast(~(CS_FOREGROUND|CS_BACKGROUND|CS_ATTRS)); mux_assert((Mask & cs) == 0); mux_assert((CS_FOREGROUND & cs) <= CS_FG_DEFAULT); mux_assert((CS_BACKGROUND & cs) <= CS_BG_DEFAULT); } inline ColorState UpdateColorState(ColorState cs, int iColorCode, const UTF8 *pRaw = nullptr) { mux_assert(0 <= iColorCode && iColorCode <= COLOR_INDEX_LAST); if (COLOR_INDEX_FG_24_CP1 <= iColorCode) { // 24-bit color: 2-code-point encoding. // CP1 carries R high nibble + G, CP2 carries R low nibble + B. // pRaw points to the 4-byte UTF-8 sequence: F3 Bx xx xx. // mux_assert(pRaw != nullptr); unsigned int payload = mux_color_smp_payload(pRaw); unsigned int hi = payload >> 8; // 4-bit nibble (CP1) or nibble (CP2) unsigned int lo = payload & 0xFF; // G (CP1) or B (CP2) switch (iColorCode) { case COLOR_INDEX_FG_24_CP1: if (CS_FG_INDEXED & cs) { cs = (cs & ~CS_FOREGROUND) | rgb2cs(&palette[CS_FG_FIELD_INDEXED(cs)].rgb); } cs = (cs & ~CS_FOREGROUND_RED) | (static_cast(hi << 4) << 16); cs = (cs & ~CS_FOREGROUND_GREEN) | (static_cast(lo) << 8); break; case COLOR_INDEX_FG_24_CP2: if (CS_FG_INDEXED & cs) { cs = (cs & ~CS_FOREGROUND) | rgb2cs(&palette[CS_FG_FIELD_INDEXED(cs)].rgb); } { ColorState red = (cs >> 16) & 0xF0; cs = (cs & ~CS_FOREGROUND_RED) | ((red | hi) << 16); } cs = (cs & ~CS_FOREGROUND_BLUE) | static_cast(lo); break; case COLOR_INDEX_BG_24_CP1: if (CS_BG_INDEXED & cs) { cs = (cs & ~CS_BACKGROUND) | (rgb2cs(&palette[CS_BG_FIELD_INDEXED(cs)].rgb) << 32); } cs = (cs & ~CS_BACKGROUND_RED) | (static_cast(hi << 4) << 48); cs = (cs & ~CS_BACKGROUND_GREEN) | (static_cast(lo) << 40); break; case COLOR_INDEX_BG_24_CP2: if (CS_BG_INDEXED & cs) { cs = (cs & ~CS_BACKGROUND) | (rgb2cs(&palette[CS_BG_FIELD_INDEXED(cs)].rgb) << 32); } { ColorState red = (cs >> 48) & 0xF0; cs = (cs & ~CS_BACKGROUND_RED) | ((red | hi) << 48); } cs = (cs & ~CS_BACKGROUND_BLUE) | (static_cast(lo) << 32); break; } mux_assert((cs & ~CS_ALLBITS) == 0); return cs; } mux_assert(iColorCode < sizeof(aColors)/sizeof(aColors[0])); return (cs & ~aColors[iColorCode].csMask) | aColors[iColorCode].cs; } // Maximum binary transition length is: // // COLOR_RESET "\xEF\x94\x80" // + COLOR_INTENSE "\xEF\x94\x81" // + COLOR_UNDERLINE "\xEF\x94\x84" // + COLOR_BLINK "\xEF\x94\x85" // + COLOR_INVERSE "\xEF\x94\x87" // + COLOR_FG_RED "\xEF\x98\x81" // + COLOR_BG_WHITE "\xEF\x9C\x87" // // Each of the seven codes is 3 bytes or 21 bytes total. Plus two 24-bit // SMP code points per layer (4 bytes each), for FG and BG = 4 code points. // #define COLOR_MAXIMUM_BINARY_TRANSITION_LENGTH (21+4*4) // Emit a 4-byte SMP PUA color code point. // block: 0=FG CP1, 1=FG CP2, 2=BG CP1, 3=BG CP2 // payload: 12-bit value ((nibble << 8) | channel_byte) // static inline void EmitSMPColor(UTF8 *buf, unsigned int block, unsigned int payload) { buf[0] = 0xF3; buf[1] = static_cast(0xB0 + block); buf[2] = static_cast(0x80 | ((payload >> 6) & 0x3F)); buf[3] = static_cast(0x80 | (payload & 0x3F)); } // Generate the minimal color sequence that will transition from one color state // to another. // static UTF8 *ColorTransitionBinary ( ColorState csCurrent, ColorState csNext, size_t *nTransition ) { ValidateColorState(csCurrent); ValidateColorState(csNext); static UTF8 Buffer[COLOR_MAXIMUM_BINARY_TRANSITION_LENGTH+1]; if (csCurrent == csNext) { *nTransition = 0; Buffer[0] = '\0'; return Buffer; } size_t i = 0; // Do we need to go through the normal state? // if ( ((csCurrent & ~csNext) & CS_ATTRS) || ( (csNext & CS_BACKGROUND) == CS_BG_DEFAULT && (csCurrent & CS_BACKGROUND) != CS_BG_DEFAULT) || ( (csNext & CS_FOREGROUND) == CS_FG_DEFAULT && (csCurrent & CS_FOREGROUND) != CS_FG_DEFAULT)) { memcpy(Buffer + i, COLOR_RESET, sizeof(COLOR_RESET)-1); i += sizeof(COLOR_RESET)-1; csCurrent = CS_NORMAL; } ColorState tmp = csCurrent ^ csNext; if (CS_ATTRS & tmp) { for (unsigned int iAttr = COLOR_INDEX_ATTR; iAttr < COLOR_INDEX_FG; iAttr++) { if (aColors[iAttr].cs == (aColors[iAttr].csMask & tmp)) { memcpy(Buffer + i, aColors[iAttr].pUTF, aColors[iAttr].nUTF); i += aColors[iAttr].nUTF; } } } RGB rgb; unsigned int iColor; if (CS_FOREGROUND & tmp) { bool fExact; if (CS_FG_INDEXED & csNext) { iColor = static_cast(CS_FG_FIELD(csNext)); fExact = true; } else { cs2rgb(CS_FG_FIELD(csNext), &rgb); iColor = FindNearestPaletteEntry(rgb, true); fExact = ( palette[iColor].rgb.r == rgb.r && palette[iColor].rgb.g == rgb.g && palette[iColor].rgb.b == rgb.b); } if (iColor < COLOR_INDEX_DEFAULT) { memcpy(Buffer + i, aColors[COLOR_INDEX_FG + iColor].pUTF, aColors[COLOR_INDEX_FG + iColor].nUTF); i += aColors[COLOR_INDEX_FG + iColor].nUTF; if (!fExact) { // 2-code-point FG encoding: CP1 (block 0) + CP2 (block 1) // EmitSMPColor(Buffer + i, 0, ((rgb.r >> 4) << 8) | rgb.g); i += 4; EmitSMPColor(Buffer + i, 1, ((rgb.r & 0xF) << 8) | rgb.b); i += 4; } } } if (CS_BACKGROUND & tmp) { bool fExact; if (CS_BG_INDEXED & csNext) { iColor = static_cast(CS_BG_FIELD(csNext)); fExact = true; } else { cs2rgb(CS_BG_FIELD(csNext), &rgb); iColor = FindNearestPaletteEntry(rgb, true); fExact = ( palette[iColor].rgb.r == rgb.r && palette[iColor].rgb.g == rgb.g && palette[iColor].rgb.b == rgb.b); } if (iColor < COLOR_INDEX_DEFAULT) { memcpy(Buffer + i, aColors[COLOR_INDEX_BG + iColor].pUTF, aColors[COLOR_INDEX_BG + iColor].nUTF); i += aColors[COLOR_INDEX_BG + iColor].nUTF; if (!fExact) { // 2-code-point BG encoding: CP1 (block 2) + CP2 (block 3) // EmitSMPColor(Buffer + i, 2, ((rgb.r >> 4) << 8) | rgb.g); i += 4; EmitSMPColor(Buffer + i, 3, ((rgb.r & 0xF) << 8) | rgb.b); i += 4; } } } Buffer[i] = '\0'; *nTransition = i; return Buffer; } // Maximum binary transition to normal is: // // COLOR_RESET "\xEF\x94\x80" // // The code is 3 bytes. // #define COLOR_MAXIMUM_BINARY_NORMAL 3 // Generate the minimal color sequence that will transition from one color state // to the normal state. // static const UTF8 *ColorBinaryNormal ( ColorState csCurrent, size_t *nTransition ) { ValidateColorState(csCurrent); if (csCurrent == CS_NORMAL) { *nTransition = 0; return T(""); } else { *nTransition = sizeof(COLOR_RESET) - 1; return T(COLOR_RESET); } } // Maximum escape transition length is: // // COLOR_RESET "%xn" // + COLOR_INTENSE "%xh" // + COLOR_UNDERLINE "%xu" // + COLOR_BLINK "%xf" // + COLOR_INVERSE "%xi" // + COLOR_FG_... "%x<#FFFFFF>" // + COLOR_BG_... "%x<#FFFFFF>" // // Each of the five codes is 3 bytes, and two codes are 11 bytes, or 37 bytes total. // #define COLOR_MAXIMUM_ESCAPE_TRANSITION_LENGTH 37 // Generate the minimal color %-sequence that will transition from one color state // to another. // static UTF8 *ColorTransitionEscape ( ColorState csCurrent, ColorState csNext, size_t *nTransition ) { ValidateColorState(csCurrent); ValidateColorState(csNext); static UTF8 Buffer[COLOR_MAXIMUM_ESCAPE_TRANSITION_LENGTH+1]; if (csCurrent == csNext) { *nTransition = 0; Buffer[0] = '\0'; return Buffer; } size_t i = 0; // Do we need to go through the normal state? // if ( ((csCurrent & ~csNext) & CS_ATTRS) || ( (csNext & CS_BACKGROUND) == CS_BG_DEFAULT && (csCurrent & CS_BACKGROUND) != CS_BG_DEFAULT) || ( (csNext & CS_FOREGROUND) == CS_FG_DEFAULT && (csCurrent & CS_FOREGROUND) != CS_FG_DEFAULT)) { memcpy(Buffer + i, aColors[COLOR_INDEX_RESET].pEscape, aColors[COLOR_INDEX_RESET].nEscape); i += aColors[COLOR_INDEX_RESET].nEscape; csCurrent = CS_NORMAL; } ColorState tmp = csCurrent ^ csNext; if (CS_ATTRS & tmp) { for (unsigned int iAttr = COLOR_INDEX_ATTR; iAttr < COLOR_INDEX_FG; iAttr++) { if (aColors[iAttr].cs == (aColors[iAttr].csMask & tmp)) { memcpy(Buffer + i, aColors[iAttr].pEscape, aColors[iAttr].nEscape); i += aColors[iAttr].nEscape; } } } RGB rgb; unsigned int iColor; if (CS_FOREGROUND & tmp) { if (CS_FG_INDEXED & csNext) { iColor = COLOR_INDEX_FG + static_cast(CS_FG_FIELD(csNext)); if (iColor < COLOR_INDEX_FG + COLOR_INDEX_DEFAULT) { memcpy(Buffer + i, aColors[iColor].pEscape, aColors[iColor].nEscape); i += aColors[iColor].nEscape; } } else { cs2rgb(CS_FG_FIELD(csNext), &rgb); mux_sprintf(Buffer + i, 12, T("%%x<#%02X%02X%02X>"), rgb.r, rgb.g, rgb.b); i += 11; } } if (CS_BACKGROUND & tmp) { if (CS_BG_INDEXED & csNext) { iColor = COLOR_INDEX_BG + static_cast(CS_BG_FIELD(csNext)); if (iColor < COLOR_INDEX_BG + COLOR_INDEX_DEFAULT) { memcpy(Buffer + i, aColors[iColor].pEscape, aColors[iColor].nEscape); i += aColors[iColor].nEscape; } } else { cs2rgb(CS_BG_FIELD(csNext), &rgb); mux_sprintf(Buffer + i, 12, T("%%X<#%02X%02X%02X>"), rgb.r, rgb.g, rgb.b); i += 11; } } Buffer[i] = '\0'; *nTransition = i; return Buffer; } // Maximum ANSI transition length is: // // ANSI_NORMAL "\033[0m" // + ANSI_HILITE "\033[1m" // + ANSI_UNDER "\033[4m" // + ANSI_BLINK "\033[5m" // + ANSI_INVERSE "\033[7m" // + ANSI_FG_... "\033[38;5;255m" // + ANSI_BWHITE "\033[48;5;255m" // // Five of the seven codes are 4 bytes, and two are 11 bytes, or 42 bytes total. // #define COLOR_MAXIMUM_ANSI_TRANSITION_LENGTH 42 // Generate the minimal ANSI sequence that will transition from one color state // to another. // static UTF8 *ColorTransitionANSI ( ColorState &csClient, ColorState csNext, size_t *nTransition, bool fNoBleed, bool fColor256 ) { ValidateColorState(csClient); ValidateColorState(csNext); static UTF8 Buffer[COLOR_MAXIMUM_ANSI_TRANSITION_LENGTH+1]; // First, modify our sense of what 'normal' is. // if ( fNoBleed && (csNext & CS_FOREGROUND) == CS_FG_DEFAULT) { csNext = (csNext & ~CS_FOREGROUND) | CS_FG_WHITE; } // Approximate Foreground Color // RGB rgb; unsigned int iColor; if (fColor256) { if (CS_FG_INDEXED & csNext) { if (CS_FG_FIELD(csNext) < COLOR_INDEX_DEFAULT) { iColor = COLOR_INDEX_FG + static_cast(CS_FG_FIELD(csNext)); } else { iColor = COLOR_INDEX_FG + COLOR_INDEX_DEFAULT; } } else { cs2rgb(CS_FG_FIELD(csNext), &rgb); iColor = COLOR_INDEX_FG + FindNearestPaletteEntry(rgb, true); } } else { if (CS_FG_INDEXED & csNext) { if (CS_FG_FIELD(csNext) < COLOR_INDEX_DEFAULT) { iColor = COLOR_INDEX_FG + palette[CS_FG_FIELD(csNext)].color16; } else { iColor = COLOR_INDEX_FG + COLOR_INDEX_DEFAULT; } } else { cs2rgb(CS_FG_FIELD(csNext), &rgb); iColor = COLOR_INDEX_FG + FindNearestPaletteEntry(rgb, false); } // For foreground 256-to-16-color down-conversion, we 'borrow' the // highlite capability. We don't need or want it if the client supports // 256-color, and there is no highlite capability we can borrow for // background color. This decision is a prerequisite to the 'return // to normal' decision below. // if (COLOR_INDEX_FG + 8 <= iColor && iColor <= COLOR_INDEX_FG + 15) { csNext |= CS_INTENSE; iColor -= 8; } } if (iColor < COLOR_INDEX_FG + COLOR_INDEX_DEFAULT) { csNext = UpdateColorState(csNext, iColor); } // Aproximate Background Color // if (fColor256) { if (CS_BG_INDEXED & csNext) { if (CS_BG_FIELD(csNext) < COLOR_INDEX_DEFAULT) { iColor = COLOR_INDEX_BG + static_cast(CS_BG_FIELD(csNext)); } else { iColor = COLOR_INDEX_BG + COLOR_INDEX_DEFAULT; } } else { cs2rgb(CS_BG_FIELD(csNext), &rgb); iColor = COLOR_INDEX_BG + FindNearestPaletteEntry(rgb, true); } } else { if (CS_BG_INDEXED & csNext) { if (CS_BG_FIELD(csNext) < COLOR_INDEX_DEFAULT) { iColor = COLOR_INDEX_BG + palette[CS_BG_FIELD(csNext)].color8; } else { iColor = COLOR_INDEX_BG + COLOR_INDEX_DEFAULT; } } else { cs2rgb(CS_BG_FIELD(csNext), &rgb); iColor = COLOR_INDEX_BG + FindNearestPalette8Entry(rgb); } } if (iColor < COLOR_INDEX_BG + COLOR_INDEX_DEFAULT) { csNext = UpdateColorState(csNext, iColor); } if (csClient == csNext) { *nTransition = 0; Buffer[0] = '\0'; return Buffer; } size_t i = 0; // Do we need to go through the normal state? // if ( ((csClient & ~csNext) & CS_ATTRS) || ( (csNext & CS_BACKGROUND) == CS_BG_DEFAULT && (csClient & CS_BACKGROUND) != CS_BG_DEFAULT) || ( (csNext & CS_FOREGROUND) == CS_FG_DEFAULT && (csClient & CS_FOREGROUND) != CS_FG_DEFAULT)) { memcpy(Buffer + i, ANSI_NORMAL, sizeof(ANSI_NORMAL)-1); i += sizeof(ANSI_NORMAL)-1; csClient = CS_NORMAL; } ColorState tmp = csClient ^ csNext; if (CS_ATTRS & tmp) { for (unsigned int iAttr = COLOR_INDEX_ATTR; iAttr < COLOR_INDEX_FG; iAttr++) { if ( g_no_flash && COLOR_INDEX_BLINK == iAttr) { continue; } if (aColors[iAttr].cs == (aColors[iAttr].csMask & tmp)) { memcpy(Buffer + i, aColors[iAttr].pAnsi, aColors[iAttr].nAnsi); i += aColors[iAttr].nAnsi; } } } // At this point, all colors are indexed. // if (CS_FOREGROUND & tmp) { iColor = COLOR_INDEX_FG + static_cast(CS_FG_FIELD(csNext)); if (iColor < COLOR_INDEX_FG + COLOR_INDEX_DEFAULT) { memcpy(Buffer + i, aColors[iColor].pAnsi, aColors[iColor].nAnsi); i += aColors[iColor].nAnsi; } } if (CS_BACKGROUND & tmp) { iColor = COLOR_INDEX_BG + static_cast(CS_BG_FIELD(csNext)); if (iColor < COLOR_INDEX_BG + COLOR_INDEX_DEFAULT) { memcpy(Buffer + i, aColors[iColor].pAnsi, aColors[iColor].nAnsi); i += aColors[iColor].nAnsi; } } Buffer[i] = '\0'; *nTransition = i; csClient = csNext; return Buffer; } void LettersToColorState(ColorState &cs, const UTF8 *pIn) { cs = CS_NORMAL; bool fBackground = false; size_t i = 0; while ('\0' != pIn[i]) { unsigned int iColor; unsigned ch = pIn[i]; if ('<' == ch) { i++; size_t j = i; while ( '\0' != (ch = pIn[i]) && '>' != ch) { i++; } RGB rgb; if ('>' == ch) { if (parse_rgb(i - j, pIn + j, rgb)) { iColor = FindNearestPaletteEntry(rgb, true); bool fExact = ( palette[iColor].rgb.r == rgb.r && palette[iColor].rgb.g == rgb.g && palette[iColor].rgb.b == rgb.b); if (fBackground) { cs = (cs & ~CS_BACKGROUND) | (fExact ? CS_BG(iColor) : (rgb2cs(&rgb) << 32)); } else { cs = (cs & ~CS_FOREGROUND) | (fExact ? CS_FG(iColor) : rgb2cs(&rgb)); } } i++; } fBackground = false; } else if ('/' == ch) { fBackground = true; i++; } else { iColor = ColorTable[pIn[i]]; if (iColor) { cs = UpdateColorState(cs, iColor); } fBackground = false; i++; } } } UTF8 *LettersToBinary(const UTF8 *pLetters) { size_t n; ColorState cs = CS_NORMAL; LettersToColorState(cs, pLetters); return ColorTransitionBinary(CS_NORMAL, cs, &n); } enum class HTMLtag { kIntense, kUnderline, kBlink, kInverse, kColor, kNormal, }; typedef struct { bool fBeginEnd; HTMLtag kTag; ColorState cs; int iStart; } tag_node; UTF8 *convert_to_html(const UTF8 *pString) { // Converting to HTML is performed in two passes. The first pass // determines an optimal nesting order of tags. The second pass generates // the HTML. // tag_node List[(LBUF_SIZE-1)/3]; int nList = 0; int Stack[10]; int nStack = 0; HTMLtag tagmap[5] = {HTMLtag::kIntense, HTMLtag::kUnderline, HTMLtag::kBlink, HTMLtag::kInverse, HTMLtag::kColor }; int iCopy; int i = 0; ColorState csPrev = CS_NORMAL; ColorState csNext = CS_NORMAL; unsigned int iCode = COLOR_NOTCOLOR; if ('\0' != pString[i]) { iCode = mux_color(pString + i); if (COLOR_NOTCOLOR == iCode) { List[nList].fBeginEnd = true; List[nList].kTag = HTMLtag::kNormal; List[nList].cs = CS_NORMAL; List[nList].iStart = -1; nList++; } } while ( '\0' != pString[i] || csNext != csPrev) { iCopy = i; while ( '\0' != pString[i] && COLOR_NOTCOLOR == iCode) { i += utf8_advance_nul(pString + i); iCode = mux_color(pString + i); } while (csNext != csPrev) { for (unsigned int iAttr = COLOR_INDEX_ATTR; iAttr < COLOR_INDEX_FG + 1; iAttr++) { ColorState tmp = csNext ^ csPrev; HTMLtag kNext = tagmap[iAttr - COLOR_INDEX_ATTR]; bool fOpen = false; bool fClose = false; if (iAttr < COLOR_INDEX_FG) { // Attribute // if (tmp & aColors[iAttr].csMask) { if (csNext & aColors[iAttr].csMask) { fOpen = true; } else { fClose = true; } } } else { // Color // if (tmp & CS_FOREGROUND) { if ( (csPrev & CS_FOREGROUND) == CS_FG_DEFAULT && (csNext & CS_FOREGROUND) != CS_FG_DEFAULT) { fOpen = true; } else if ( (csPrev & CS_FOREGROUND) != CS_FG_DEFAULT && (csNext & CS_FOREGROUND) == CS_FG_DEFAULT) { fClose = true; } else { fOpen = true; fClose = true; } } if (tmp & CS_BACKGROUND) { if ( (csPrev & CS_BACKGROUND) == CS_BG_DEFAULT && (csNext & CS_BACKGROUND) != CS_BG_DEFAULT) { fOpen = true; } else if ( (csPrev & CS_BACKGROUND) != CS_BG_DEFAULT && (csNext & CS_BACKGROUND) == CS_BG_DEFAULT) { fClose = true; } else { fOpen = true; fClose = true; } } } if (fClose) { for (int j = nStack - 1; 0 <= j; j--) { if (List[Stack[j]].kTag == kNext) { if (List[Stack[j]].iStart != List[Stack[nStack-1]].iStart) { // Pop other tags and mark in csPrev that they are gone. We'll add them back in the next // iteration of the loop. We stop at the point where tags can be swapped with the one // we're interested in. // for (int k = nStack - 1; j < k && List[Stack[j]].iStart != List[Stack[nStack-1]].iStart; k--) { List[nList] = List[Stack[k]]; List[nList].fBeginEnd = false; List[nList].iStart = -1; switch (List[nList].kTag) { case HTMLtag::kIntense: csPrev &= ~CS_INTENSE; break; case HTMLtag::kUnderline: csPrev &= ~CS_UNDERLINE; break; case HTMLtag::kBlink: csPrev &= ~CS_BLINK; break; case HTMLtag::kInverse: csPrev &= ~CS_INVERSE; break; case HTMLtag::kColor: csPrev = (csPrev & ~(CS_FOREGROUND|CS_BACKGROUND)) | CS_NORMAL; break; default: break; } nList++; nStack--; } } if (j != nStack - 1) { // Change the order of tags which open at the // same position so that the one we want to // close can be popped. // tag_node t = List[Stack[j]]; List[Stack[j]] = List[Stack[nStack-1]]; List[Stack[nStack-1]] = t; j = nStack - 1; } List[nList] = List[Stack[j]]; List[nList].fBeginEnd = false; List[nList].iStart = -1; switch (List[nList].kTag) { case HTMLtag::kIntense: csPrev &= ~CS_INTENSE; break; case HTMLtag::kUnderline: csPrev &= ~CS_UNDERLINE; break; case HTMLtag::kBlink: csPrev &= ~CS_BLINK; break; case HTMLtag::kInverse: csPrev &= ~CS_INVERSE; break; case HTMLtag::kColor: csPrev = (csPrev & ~(CS_FOREGROUND|CS_BACKGROUND)) | CS_NORMAL; break; default: break; } nList++; nStack--; if (0 == nStack) { List[nList].fBeginEnd = true; List[nList].kTag = HTMLtag::kNormal; List[nList].cs = CS_NORMAL; List[nList].iStart = -1; nList++; } break; } } } if (fOpen) { List[nList].fBeginEnd = true; List[nList].cs = csNext; List[nList].kTag = kNext; List[nList].iStart = -1; switch (List[nList].kTag) { case HTMLtag::kIntense: csPrev |= CS_INTENSE; break; case HTMLtag::kUnderline: csPrev |= CS_UNDERLINE; break; case HTMLtag::kBlink: csPrev |= CS_BLINK; break; case HTMLtag::kInverse: csPrev |= CS_INVERSE; break; case HTMLtag::kColor: csPrev &= ~(CS_FOREGROUND|CS_BACKGROUND); csPrev |= (CS_FOREGROUND|CS_BACKGROUND) & csNext;; break; default: break; } Stack[nStack++] = nList++; } } } if (0 < nList) { List[nList-1].iStart = iCopy; } while ( '\0' != pString[i] && COLOR_NOTCOLOR != iCode) { csNext = UpdateColorState(csNext, iCode, pString + i); i += utf8_advance_nul(pString + i); iCode = mux_color(pString + i); } // When CS_INTENSE is active and the foreground is a basic ANSI // color (0-7), substitute the bright palette entry (8-15) so // that HTML tags carry the correct RGB value. // if ( (csNext & CS_INTENSE) && (CS_FG_INDEXED & csNext)) { unsigned int idx = static_cast(CS_FG_FIELD(csNext)); if (idx < 8) { csNext = (csNext & ~CS_FOREGROUND) | CS_FG(idx + 8); } } } static UTF8 aBuffer[2*LBUF_SIZE]; UTF8 *pBuffer = aBuffer; for (int iList = 0; iList < nList; iList++) { if (HTMLtag::kNormal != List[iList].kTag) { *pBuffer++ = '<'; if (!List[iList].fBeginEnd) { *pBuffer++ = '/'; } switch (List[iList].kTag) { case HTMLtag::kIntense: *pBuffer++ = 'B'; break; case HTMLtag::kUnderline: *pBuffer++ = 'U'; break; case HTMLtag::kBlink: *pBuffer++ = 'I'; break; case HTMLtag::kInverse: *pBuffer++ = 'S'; break; case HTMLtag::kColor: if (List[iList].fBeginEnd) { ColorState cs = List[iList].cs; if ( (CS_FOREGROUND & cs) != CS_FG_DEFAULT && (CS_FG_INDEXED & cs)) { cs = (cs & ~CS_FOREGROUND) | rgb2cs(&palette[CS_FG_FIELD(cs)].rgb); } if ( (CS_BACKGROUND & cs) != CS_BG_DEFAULT && (CS_BG_INDEXED & cs)) { cs = (cs & ~CS_BACKGROUND) | (rgb2cs(&palette[CS_BG_FIELD(cs)].rgb) << 32); } if ((CS_FOREGROUND & cs) == CS_FG_DEFAULT) { if ((CS_BACKGROUND & cs) != CS_BG_DEFAULT) { mux_sprintf(pBuffer, sizeof(aBuffer) - (pBuffer - aBuffer) - 1, T("COLOR BACK=#%06llX"), CS_BG_FIELD(cs)); pBuffer += strlen(reinterpret_cast(pBuffer)); } } else { if ((CS_BACKGROUND & cs) == CS_BG_DEFAULT) { mux_sprintf(pBuffer, sizeof(aBuffer) - (pBuffer - aBuffer) - 1, T("COLOR #%06llX"), CS_FG_FIELD(cs)); pBuffer += strlen(reinterpret_cast(pBuffer)); } else { mux_sprintf(pBuffer, sizeof(aBuffer) - (pBuffer - aBuffer) - 1, T("COLOR #%06llX #%06llX"), CS_FG_FIELD(cs), CS_BG_FIELD(cs)); pBuffer += strlen(reinterpret_cast(pBuffer)); } } } else { mux_sprintf(pBuffer, sizeof(aBuffer) - (pBuffer - aBuffer) - 1, T("COLOR")); pBuffer += strlen(reinterpret_cast(pBuffer)); } break; default: break; } *pBuffer++ = '>'; } iCopy = i = List[iList].iStart; if (0 <= iCopy) { if ('\0' != pString[i]) { iCode = mux_color(pString + i); } while ( '\0' != pString[i] && COLOR_NOTCOLOR == iCode) { i += utf8_advance_nul(pString + i); iCode = mux_color(pString + i); } size_t n = i - iCopy; if (0 < n) { memcpy(pBuffer, pString + List[iList].iStart, n); pBuffer += n; } } } *pBuffer = '\0'; return aBuffer; } /*! \brief Convert private color code points within a string to ANSI color * sequences. * * This routine converts these internal color code points to ANSI sequences * which clients will recognize. * * Internally, the server uses color code points to represent color. This * representation is more compact, it simplifies the parser, and it thwarts * the possibility of softcode generating naked ESC characters. Several * protocols use the ESC character, so it is helpful if the server controls * their use. * * \param pString String (length LBUF_SIZE-1 or less) with color code points. * \param fNoBleed Should we dodge a naked return-to-normal state. * \param fColor256 Can we use the full 256-color palette? * \return String with exportable ANSI color sequences. */ UTF8 *convert_color(const UTF8 *pString, bool fNoBleed, bool fColor256) { static UTF8 aBuffer[2*LBUF_SIZE]; static const UTF8 *pNormal= T(ANSI_NORMAL ANSI_WHITE); size_t nNormal = sizeof(ANSI_NORMAL)-1; if (fNoBleed) { nNormal += sizeof(ANSI_WHITE)-1; } ColorState csClient = (fNoBleed)?CS_FG_WHITE:CS_NORMAL; ColorState csPrev = CS_NORMAL; ColorState csNext = CS_NORMAL; UTF8 *pBuffer = aBuffer; size_t i = 0; size_t iCopy = 0; unsigned int iCode = COLOR_NOTCOLOR; if ('\0' != pString[i]) { iCode = mux_color(pString + i); } while ('\0' != pString[i]) { iCopy = i; while ( '\0' != pString[i] && COLOR_NOTCOLOR == iCode) { i += utf8_advance_nul(pString + i); iCode = mux_color(pString + i); } if (iCopy < i) { if (csNext != csPrev) { UTF8 *pTransition = nullptr; size_t nTransition; pTransition = ColorTransitionANSI( csClient, csNext, &nTransition, fNoBleed, fColor256); if (nTransition) { if (sizeof(aBuffer) - (pBuffer - aBuffer) - nNormal - 1 < nTransition) { break; } memcpy(pBuffer, pTransition, nTransition); pBuffer += nTransition; } csPrev = csNext; } if (sizeof(aBuffer) - (pBuffer - aBuffer) - nNormal - 1 < i - iCopy) { break; } memcpy(pBuffer, pString + iCopy, i - iCopy); pBuffer += i - iCopy; iCopy = i; } while ( '\0' != pString[i] && COLOR_NOTCOLOR != iCode) { csNext = UpdateColorState(csNext, iCode, pString + i); i += utf8_advance_nul(pString + i); iCode = mux_color(pString + i); } } if (csPrev != CS_NORMAL) { memcpy(pBuffer, pNormal, nNormal); pBuffer += nNormal; } *pBuffer = '\0'; return aBuffer; } UTF8 *strip_color(const UTF8 *pString, size_t *pnBytes, size_t *pnPoints) { static UTF8 aBuffer[LBUF_SIZE]; UTF8 *pBuffer = aBuffer; if (nullptr == pString) { if (nullptr != pnBytes) { *pnBytes = 0; } if (nullptr != pnPoints) { *pnPoints = 0; } *pBuffer = '\0'; return aBuffer; } size_t nPoints = 0; while ('\0' != *pString) { if (COLOR_NOTCOLOR == mux_color(pString)) { utf8_safe_chr(pString, aBuffer, &pBuffer); nPoints++; } pString += utf8_advance_nul(pString); } *pBuffer = '\0'; if (nullptr != pnBytes) { *pnBytes = pBuffer - aBuffer; } if (nullptr != pnPoints) { *pnPoints = nPoints; } return aBuffer; } typedef struct { int len; const UTF8 *p; } LITERAL_STRING_STRUCT; #define NUM_WS_SUBS 6 #define NUM_MU_SUBS 14 static LITERAL_STRING_STRUCT MU_Substitutes[NUM_MU_SUBS] = { { 1, T(" ") }, // 0 { 1, T(" ") }, // 1 { 2, T("%t") }, // 2 { 2, T("%r") }, // 3 { 0, T("") }, // 4 -- emit nothing (\r); empty, not null (#1458) { 2, T("%b") }, // 5 { 2, T("%%") }, // 6 { 2, T("%(") }, // 7 { 2, T("%)") }, // 8 { 2, T("%[") }, // 9 { 2, T("%]") }, // 10 { 2, T("%{") }, // 11 { 2, T("%}") }, // 12 { 2, T("\\\\") } // 13 }; const unsigned char MU_EscapeConvert[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 4, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 0, 0, 0, 0, 6, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,13,10, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,11, 0,12, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; const unsigned char MU_EscapeNoConvert[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // Convert raw character sequences into MUX substitutions (type = 1) // or strips them (type = 0). // UTF8 *translate_string(const UTF8 *pString, bool bConvert) { static UTF8 szTranslatedString[LBUF_SIZE]; UTF8 *pTranslatedString = szTranslatedString; if (!pString) { *pTranslatedString = '\0'; return szTranslatedString; } ColorState csCurrent = CS_NOBLEED; ColorState csPrevious = csCurrent; const UTF8 *MU_EscapeChar = (bConvert)? MU_EscapeConvert : MU_EscapeNoConvert; while ('\0' != *pString) { unsigned int iCode = mux_color(pString); if (COLOR_NOTCOLOR == iCode) { size_t nTransition = 0; if (bConvert) { UTF8 *pTransition = ColorTransitionEscape(csPrevious, csCurrent, &nTransition); safe_str(pTransition, szTranslatedString, &pTranslatedString); csPrevious = csCurrent; } UTF8 ch = pString[0]; unsigned char code = MU_EscapeChar[ch]; if ( 0 < code && code < NUM_MU_SUBS) { if ( ' ' == ch && ' ' == pString[1]) { code = 5; } safe_copy_buf(MU_Substitutes[code].p, MU_Substitutes[code].len, szTranslatedString, &pTranslatedString); } else { utf8_safe_chr(pString, szTranslatedString, &pTranslatedString); } } else { csCurrent = UpdateColorState(csCurrent, iCode, pString); } pString += utf8_advance_nul(pString); } *pTranslatedString = '\0'; return szTranslatedString; } bool IsDecompFriendly(const UTF8 *pString) { bool fFriendly = true; if (!pString) { return fFriendly; } const UTF8 *MU_EscapeChar = MU_EscapeConvert; while ( '\0' != *pString && fFriendly) { unsigned int iCode = mux_color(pString); if (COLOR_NOTCOLOR == iCode) { UTF8 ch = pString[0]; unsigned char code = MU_EscapeChar[ch]; if ( 0 < code && code < NUM_WS_SUBS) { if (' ' == ch) { if (' ' == pString[1]) { // Two adjacent spaces are not @decomp-friendly. // fFriendly = false; break; } // An isolated space is still @decomp-friendly. // } else { // Raw tabs and newlines are not @decomp-friendly. // fFriendly = false; break; } } } else { // Raw color codes are not @decomp-friendly. // fFriendly = false; break; } pString += utf8_advance_nul(pString); } return fFriendly; } /* --------------------------------------------------------------------------- * munge_space: Compress multiple spaces to one space, also remove leading and * trailing spaces. */ UTF8 *munge_space(const UTF8 *string) { UTF8 *buffer = alloc_lbuf("munge_space"); const UTF8 *p = string; UTF8 *q = buffer; if (p) { // Remove initial spaces. // while (mux_isspace(*p)) p++; while (*p) { while (*p && !mux_isspace(*p)) *q++ = *p++; while (mux_isspace(*p)) { p++; } if (*p) *q++ = ' '; } } // Remove terminal spaces and terminate string. // *q = '\0'; return buffer; } /* --------------------------------------------------------------------------- * trim_spaces: Remove leading and trailing spaces and space-compress internal * spaces. */ UTF8 *trim_spaces(const UTF8 *string) { UTF8 *buffer = alloc_lbuf("trim_spaces"); const UTF8 *p = string; UTF8 *q = buffer; if (p) { // Remove initial spaces. // while (mux_isspace(*p)) { p++; } while (*p) { // Copy non-space characters. // while (*p && !mux_isspace(*p)) { *q++ = *p++; } // Compress spaces. // while (mux_isspace(*p)) { p++; } // Leave one space. // if (*p) { *q++ = ' '; } } } // Terminate string. // *q = '\0'; return buffer; } /* * --------------------------------------------------------------------------- * * grabto: Return portion of a string up to the indicated character. Also * * returns a modified pointer to the string ready for another call. */ UTF8 *grabto(UTF8 **str, UTF8 targ) { UTF8 *savec, *cp; if (!str || !*str || !**str) return nullptr; savec = cp = *str; while (*cp && *cp != targ) cp++; if (*cp) *cp++ = '\0'; *str = cp; return savec; } int string_compare(const UTF8 *s1, const UTF8 *s2) { if (g_space_compress) { while (mux_isspace(*s1)) { s1++; } while (mux_isspace(*s2)) { s2++; } while (*s1 && *s2) { if (mux_isspace(*s1) && mux_isspace(*s2)) { // Both are spaces — skip all contiguous spaces. // do { s1++; } while (mux_isspace(*s1)); do { s2++; } while (mux_isspace(*s2)); continue; } // ASCII fast path: compare via table without the full // Unicode state machine. // if (*s1 < 0x80 && *s2 < 0x80) { if (mux_tolower_ascii[*s1] != mux_tolower_ascii[*s2]) { break; } s1++; s2++; continue; } // Compare code points case-insensitively (Unicode-aware). // UTF8 la[4], lb[4]; size_t na, nb; bool bXorA; const string_desc *qA = mux_tolower(s1, bXorA); if (nullptr == qA) { na = utf8_FirstByte[static_cast(*s1)]; if (na >= UTF8_CONTINUE) na = 1; for (size_t j = 0; j < na; j++) la[j] = s1[j]; } else { na = qA->n_bytes; if (bXorA) { for (size_t j = 0; j < na; j++) la[j] = s1[j] ^ qA->p[j]; } else { for (size_t j = 0; j < na; j++) la[j] = qA->p[j]; } } bool bXorB; const string_desc *qB = mux_tolower(s2, bXorB); if (nullptr == qB) { nb = utf8_FirstByte[static_cast(*s2)]; if (nb >= UTF8_CONTINUE) nb = 1; for (size_t j = 0; j < nb; j++) lb[j] = s2[j]; } else { nb = qB->n_bytes; if (bXorB) { for (size_t j = 0; j < nb; j++) lb[j] = s2[j] ^ qB->p[j]; } else { for (size_t j = 0; j < nb; j++) lb[j] = qB->p[j]; } } // If lowercased code points differ, we're done. // if (na != nb) { break; } bool bEqual = true; for (size_t j = 0; j < na; j++) { if (la[j] != lb[j]) { bEqual = false; break; } } if (!bEqual) { break; } s1 += na; s2 += nb; } if ( *s1 && *s2) { return 1; } if (mux_isspace(*s1)) { while (mux_isspace(*s1)) { s1++; } return *s1; } if (mux_isspace(*s2)) { while (mux_isspace(*s2)) { s2++; } return *s2; } if ( *s1 || *s2) { return 1; } return 0; } else { return mux_stricmp(s1, s2); } } int string_prefix(const UTF8 *string, const UTF8 *prefix) { int count = 0; // ASCII fast path: compare bytes directly via tolower table. // Most object names and commands are pure ASCII — this avoids // the full Unicode mux_tolower state machine per character. // while (*string && *prefix && *string < 0x80 && *prefix < 0x80) { if (mux_tolower_ascii[*string] != mux_tolower_ascii[*prefix]) { return 0; } string++; prefix++; count++; } // Unicode fallback: code-point-at-a-time case-insensitive comparison. // while (*string && *prefix) { UTF8 la[4], lb[4]; size_t na, nb; bool bXorA; const string_desc *qA = mux_tolower(string, bXorA); if (nullptr == qA) { na = utf8_FirstByte[static_cast(*string)]; if (na >= UTF8_CONTINUE) na = 1; for (size_t j = 0; j < na; j++) la[j] = string[j]; } else { na = qA->n_bytes; if (bXorA) { for (size_t j = 0; j < na; j++) la[j] = string[j] ^ qA->p[j]; } else { for (size_t j = 0; j < na; j++) la[j] = qA->p[j]; } } bool bXorB; const string_desc *qB = mux_tolower(prefix, bXorB); if (nullptr == qB) { nb = utf8_FirstByte[static_cast(*prefix)]; if (nb >= UTF8_CONTINUE) nb = 1; for (size_t j = 0; j < nb; j++) lb[j] = prefix[j]; } else { nb = qB->n_bytes; if (bXorB) { for (size_t j = 0; j < nb; j++) lb[j] = prefix[j] ^ qB->p[j]; } else { for (size_t j = 0; j < nb; j++) lb[j] = qB->p[j]; } } if (na != nb) { return 0; } for (size_t j = 0; j < na; j++) { if (la[j] != lb[j]) { return 0; } } string += na; prefix += nb; count += static_cast(na); } if (*prefix == '\0') { // Matched all of prefix. // return count; } else { return 0; } } /* * accepts only nonempty matches starting at the beginning of a word */ const UTF8 *string_match(const UTF8 *src, const UTF8 *sub) { if ((*sub != '\0') && (src)) { const bool ascii_sub = (*sub < 0x80); const UTF8 sub0_lower = ascii_sub ? mux_tolower_ascii[*sub] : 0; while (*src) { if (ascii_sub && *src < 0x80) { if (mux_tolower_ascii[*src] != sub0_lower) { while (mux_isalnum(*src)) { src++; } while (*src && !mux_isalnum(*src)) { src++; } continue; } const UTF8 *s = src; const UTF8 *p = sub; while (*s && *p && *s < 0x80 && *p < 0x80) { if (mux_tolower_ascii[*s] != mux_tolower_ascii[*p]) { break; } s++; p++; } if (*p == '\0') { return src; } // If the ASCII prefix matched and either side then went // non-ASCII, fall back to the full Unicode-aware matcher. if ((*s || *p) && !(*s < 0x80 && *p < 0x80) && string_prefix(src, sub)) { return src; } } else if (string_prefix(src, sub)) { return src; } // else scan to beginning of next word // while (mux_isalnum(*src)) { src++; } while (*src && !mux_isalnum(*src)) { src++; } } } return 0; } /* * --------------------------------------------------------------------------- * * replace_string: Returns an lbuf containing string STRING with all occurances * * of OLD replaced by NEW. OLD and NEW may be different lengths. * * (mitch 1 feb 91) */ UTF8 *replace_string(const UTF8 *old, const UTF8 *new0, const UTF8 *s) { if (!s) { return nullptr; } size_t olen = strlen(reinterpret_cast(old)); UTF8 *result = alloc_lbuf("replace_string"); UTF8 *r = result; while (*s) { // Find next occurrence of the first character of OLD string. // const UTF8 *p = reinterpret_cast(strchr(reinterpret_cast(s), old[0])); if ( olen && p) { // Copy up to the next occurrence of the first char of OLD. // size_t n = p - s; if (n) { safe_copy_buf(s, n, result, &r); s += n; } // If we are really at an complete OLD, append NEW to the result // and bump the input string past the occurrence of OLD. // Otherwise, copy the character and try matching again. // if (!strncmp(reinterpret_cast(old), reinterpret_cast(s), olen)) { safe_str(new0, result, &r); s += olen; } else { safe_chr(*s, result, &r); s++; } } else { // Finish copying source string. No matches. No further // work to perform. // safe_str(s, result, &r); break; } } *r = '\0'; return result; } bool minmatch(const UTF8 *str, const UTF8 *target, int min) { // Code-point-at-a-time case-insensitive comparison (Unicode-aware). // min tracks bytes matched (callers pass byte-based minlen). // while (*str && *target) { UTF8 la[4], lb[4]; size_t na, nb; bool bXorA; const string_desc *qA = mux_tolower(str, bXorA); if (nullptr == qA) { na = utf8_FirstByte[static_cast(*str)]; if (na >= UTF8_CONTINUE) na = 1; for (size_t j = 0; j < na; j++) la[j] = str[j]; } else { na = qA->n_bytes; if (bXorA) { for (size_t j = 0; j < na; j++) la[j] = str[j] ^ qA->p[j]; } else { for (size_t j = 0; j < na; j++) la[j] = qA->p[j]; } } bool bXorB; const string_desc *qB = mux_tolower(target, bXorB); if (nullptr == qB) { nb = utf8_FirstByte[static_cast(*target)]; if (nb >= UTF8_CONTINUE) nb = 1; for (size_t j = 0; j < nb; j++) lb[j] = target[j]; } else { nb = qB->n_bytes; if (bXorB) { for (size_t j = 0; j < nb; j++) lb[j] = target[j] ^ qB->p[j]; } else { for (size_t j = 0; j < nb; j++) lb[j] = qB->p[j]; } } if (na != nb) { return false; } for (size_t j = 0; j < na; j++) { if (la[j] != lb[j]) { return false; } } str += na; target += nb; min -= static_cast(na); } if (*str) { return false; } if (!*target) { return true; } return (min <= 0); } // -------------------------------------------------------------------------- // StringCloneLen: allocate memory and copy string // UTF8 *StringCloneLen(const UTF8 *str, size_t nStr) { UTF8 *buff = reinterpret_cast(MEMALLOC(nStr+1)); if (buff) { memcpy(buff, str, nStr); buff[nStr] = '\0'; } else { OutOfMemory(reinterpret_cast(__FILE__), __LINE__); } return buff; } // -------------------------------------------------------------------------- // StringClone: allocate memory and copy string // UTF8 *StringClone(const UTF8 *str) { return StringCloneLen(str, strlen(reinterpret_cast(str))); } /* --------------------------------------------------------------------------- * safe_copy_str - Copy buffers, watching for overflows. */ void safe_copy_str(const UTF8 *src, UTF8 *buff, UTF8 **bufp, size_t nSizeOfBuffer) { if (src == nullptr) return; UTF8 *tp = *bufp; UTF8 *maxtp = buff + nSizeOfBuffer; while (tp < maxtp && *src) { *tp++ = *src++; } *bufp = buff + TrimPartialSequence(tp - buff, buff); } void safe_copy_str_lbuf(const UTF8 *src, UTF8 *buff, UTF8 **bufp) { if (src == nullptr) { return; } UTF8 *tp = *bufp; UTF8 *maxtp = buff + LBUF_SIZE - 1; while (tp < maxtp && *src) { *tp++ = *src++; } *bufp = buff + TrimPartialSequence(tp - buff, buff); } size_t safe_copy_buf(const UTF8 *src, size_t nLen, UTF8 *buff, UTF8 **bufc) { // A null src is UB for memcpy even when nLen is zero -- the standard // requires valid pointers regardless of the length (C17 7.24.1p2), and // UBSan reports it as "null pointer passed as argument 2" (#1458). // // Guarding here rather than only at the one caller that does it: the // sibling helpers safe_copy_str, safe_copy_str_lbuf and utf8_safe_chr // all already return early on a null src, so tolerating it is the // established convention among these functions and safe_copy_buf was // the one outlier. It also covers the TrimPartialSequence call below, // which would dereference src outright had a caller passed null with a // non-zero length. // if (nullptr == src) { return 0; } size_t left = LBUF_SIZE - (*bufc - buff) - 1; if (left < nLen) { nLen = TrimPartialSequence(left, src); } memcpy(*bufc, src, nLen); *bufc += nLen; return nLen; } size_t safe_fill(UTF8 *buff, UTF8 **bufc, UTF8 chFill, size_t nSpaces) { // Check for buffer limits. // size_t nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1; if (nSpaces > nBufferAvailable) { nSpaces = nBufferAvailable; } // Fill with spaces. // memset(*bufc, chFill, nSpaces); *bufc += nSpaces; return nSpaces; } void utf8_safe_chr(const UTF8 *src, UTF8 *buff, UTF8 **bufc) { size_t nLen; size_t nLeft; if ( nullptr == src || UTF8_CONTINUE <= (nLen = utf8_FirstByte[*src])) { return; } for (size_t i = 1; i < nLen; i++) { if ( '\0' == src[i] || UTF8_CONTINUE != utf8_FirstByte[src[i]]) { return; } } nLeft = LBUF_SIZE - (*bufc - buff) - 1; if (nLeft < nLen) { return; } memcpy(*bufc, src, nLen); *bufc += nLen; } UTF8 *ConvertToUTF8 ( UTF32 ch ) { static UTF8 buffer[6]; const UTF32 byteMask = 0xBF; const UTF32 byteMark = 0x80; if (ch < (UTF32)0x80) { // ASCII // buffer[1] = '\0'; buffer[0] = static_cast(ch); } else if (ch < (UTF32)0x800) { buffer[2] = '\0'; buffer[1] = static_cast((ch | byteMark) & byteMask); ch >>= 6; buffer[0] = static_cast(0xC0 | ch); } else if (ch < (UTF32)0x10000) { if ( UNI_SUR_HIGH_START <= ch && ch <= UNI_SUR_LOW_END) { buffer[0] = '\0'; return buffer; } buffer[3] = '\0'; buffer[2] =static_cast((ch | byteMark) & byteMask); ch >>= 6; buffer[1] = static_cast((ch | byteMark) & byteMask); ch >>= 6; buffer[0] = static_cast(0xE0 | ch); } else if (ch <= UNI_MAX_LEGAL_UTF32) { buffer[4] = '\0'; buffer[3] = static_cast((ch | byteMark) & byteMask); ch >>= 6; buffer[2] = static_cast((ch | byteMark) & byteMask); ch >>= 6; buffer[1] = static_cast((ch | byteMark) & byteMask); ch >>= 6; buffer[0] = static_cast(0xF0 | ch); } return buffer; } UTF16 *ConvertToUTF16(UTF32 ch) { static UTF16 buffer[3]; if ( ch < UNI_SUR_HIGH_START || UNI_SUR_LOW_END < ch) { // This is the common case. // buffer[0] = (UTF16)ch; buffer[1] = 0x0000; return buffer; } else if (ch <= UNI_MAX_LEGAL_UTF32) { const int halfShift = 10; const UTF32 halfBase = 0x0010000UL; const UTF32 halfMask = 0x3FFUL; ch -= halfBase; buffer[0] = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); buffer[1] = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); buffer[2] = 0x0000; } else { buffer[0] = UNI_REPLACEMENT_CHAR; buffer[1] = 0x0000; } return buffer; } UTF32 ConvertFromUTF8(const UTF8 *pString) { size_t t = utf8_FirstByte[*pString]; if (UTF8_CONTINUE <= t) { return UNI_EOF; } UTF32 ch; if (1 == t) { // This is the most common case, and the value is always smaller than // UNI_SUR_HIGH_START. // return pString[0]; } else if (2 == t) { if ( '\0' == pString[1] || UTF8_CONTINUE != utf8_FirstByte[pString[1]]) { return UNI_EOF; } ch = utf8_decode_raw(pString, 2); if (!utf8_is_valid_scalar(ch, 2)) { return UNI_EOF; } } else if (3 == t) { if ( '\0' == pString[1] || '\0' == pString[2] || UTF8_CONTINUE != utf8_FirstByte[pString[1]] || UTF8_CONTINUE != utf8_FirstByte[pString[2]]) { return UNI_EOF; } ch = utf8_decode_raw(pString, 3); if (!utf8_is_valid_scalar(ch, 3)) { return UNI_EOF; } } else if (4 == t) { if ( '\0' == pString[1] || '\0' == pString[2] || '\0' == pString[3] || UTF8_CONTINUE != utf8_FirstByte[pString[1]] || UTF8_CONTINUE != utf8_FirstByte[pString[2]] || UTF8_CONTINUE != utf8_FirstByte[pString[3]]) { return UNI_EOF; } ch = utf8_decode_raw(pString, 4); if (!utf8_is_valid_scalar(ch, 4)) { return UNI_EOF; } } else { return UNI_EOF; } if (utf8_is_valid_scalar(ch, t)) { return ch; } else { return UNI_EOF; } } size_t ConvertFromUTF16(UTF16 *pString, UTF32 &ch) { ch = pString[0]; if ( ch < UNI_SUR_HIGH_START || UNI_SUR_LOW_END < ch) { // This is the most-common case. // return 1; } else if (ch <= UNI_SUR_HIGH_END) { UTF32 ch2 = pString[1]; if ( UNI_SUR_LOW_START <= ch2 && ch2 <= UNI_SUR_LOW_END) { const int halfShift = 10; const UTF32 halfBase = 0x0010000UL; ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase; return 2; } } ch = UNI_EOF; return 0; } UTF16 *ConvertFromUTF8ToUTF16(const UTF8 *pString, size_t& length) { static UTF16 buffer[2*LBUF_SIZE]; UTF16 *p = buffer; UTF16 *const pEnd = buffer + sizeof(buffer) / sizeof(buffer[0]); length = 0; while ('\0' != *pString) { UTF32 ch = ConvertFromUTF8(pString); if (UNI_EOF == ch) { return nullptr; } UTF16 *q = ConvertToUTF16(ch); while (0x0000 != *q) { if (p + 1 >= pEnd) { return nullptr; } *p++ = *q++; } size_t nAdvance = utf8_FirstByte[static_cast(*pString)]; if (nAdvance < 1 || nAdvance >= UTF8_CONTINUE) { return nullptr; } pString += nAdvance; } if (p >= pEnd) { return nullptr; } *p = '\0'; length = p - buffer; return buffer; } // We want to remove mal-formed ESC sequences completely and convert the // well-formed ones. // UTF8 *ConvertToUTF8(const char *p, size_t *pn) { *pn = 0; static UTF8 aBuffer[LBUF_SIZE]; UTF8 *pBuffer = aBuffer; while ('\0' != *p) { if (ESC_CHAR != *p) { const UTF8 *q = latin1_utf8[static_cast(*p)]; utf8_safe_chr(q, aBuffer, &pBuffer); p++; } else { // We have an ANSI sequence. // p++; if ('[' == *p) { p++; const char *q = p; while (ANSI_TokenTerminatorTable[static_cast(*q)] == 0) { q++; } if ('\0' != q[0]) { // The segment [p,q) should contain a list of semi-color delimited codes. // const char *r = p; while (r != q) { while ( r != q && ';' != r[0]) { r++; } // The segment [p,r) should contain one code. // size_t n = r - p; const UTF8 *s = nullptr; switch (n) { case 1: if ('0' == *p) { s = aColors[COLOR_INDEX_RESET].pUTF; } else if ('1' == *p) { s = aColors[COLOR_INDEX_INTENSE].pUTF; } else if ('4' == *p) { s = aColors[COLOR_INDEX_UNDERLINE].pUTF; } else if ('5' == *p) { s = aColors[COLOR_INDEX_BLINK].pUTF; } else if ('7' == *p) { s = aColors[COLOR_INDEX_INVERSE].pUTF; } break; case 2: if ('3' == *p) { unsigned int iCode = COLOR_INDEX_FG + (p[1] - '0'); if ( COLOR_INDEX_FG <= iCode && iCode < COLOR_INDEX_FG + 7) { s = aColors[iCode].pUTF; } } else if ('4' == *p) { unsigned int iCode = COLOR_INDEX_BG + (p[1] - '0'); if ( COLOR_INDEX_BG <= iCode && iCode <= COLOR_INDEX_BG + 7) { s = aColors[iCode].pUTF; } } break; } if (nullptr != s) { utf8_safe_chr(s, aBuffer, &pBuffer); } while ( r != q && ';' == r[0]) { r++; } p = r; } // Eat trailing terminator. // p = q + 1; } else { // Skip to end of mal-formed ANSI sequence. // p = q; } } } } *pBuffer = '\0'; *pn = pBuffer - aBuffer; return aBuffer; } // mux_strncpy: Copies up to specified number of chars from source. // Note: unlike strncpy(), this null-terminates after copying. // void mux_strncpy(UTF8 *dest, const UTF8 *src, size_t length_to_copy) { size_t i = 0; if ( nullptr != src || length_to_copy < 0) { while ( i < length_to_copy && '\0' != src[i]) { dest[i] = src[i]; i++; } } dest[i] = '\0'; } bool matches_exit_from_list(const UTF8 *str, const UTF8 *pattern) { const UTF8 *s; while (*pattern) { // Compare str against this exit name, code-point-at-a-time // (Unicode-aware). // s = str; while ( *s && *pattern && *pattern != EXIT_DELIMITER) { UTF8 la[4], lb[4]; size_t na, nb; bool bXorA; const string_desc *qA = mux_tolower(s, bXorA); if (nullptr == qA) { na = utf8_FirstByte[static_cast(*s)]; if (na >= UTF8_CONTINUE) na = 1; for (size_t j = 0; j < na; j++) la[j] = s[j]; } else { na = qA->n_bytes; if (bXorA) { for (size_t j = 0; j < na; j++) la[j] = s[j] ^ qA->p[j]; } else { for (size_t j = 0; j < na; j++) la[j] = qA->p[j]; } } bool bXorB; const string_desc *qB = mux_tolower(pattern, bXorB); if (nullptr == qB) { nb = utf8_FirstByte[static_cast(*pattern)]; if (nb >= UTF8_CONTINUE) nb = 1; for (size_t j = 0; j < nb; j++) lb[j] = pattern[j]; } else { nb = qB->n_bytes; if (bXorB) { for (size_t j = 0; j < nb; j++) lb[j] = pattern[j] ^ qB->p[j]; } else { for (size_t j = 0; j < nb; j++) lb[j] = qB->p[j]; } } if (na != nb) { break; } bool bEqual = true; for (size_t j = 0; j < na; j++) { if (la[j] != lb[j]) { bEqual = false; break; } } if (!bEqual) { break; } s += na; pattern += nb; } // Did we match it all? // if (*s == '\0') { // Make sure nothing afterwards // while (mux_isspace(*pattern)) { pattern++; } // Did we get it? // if ( !*pattern || (*pattern == EXIT_DELIMITER)) { return true; } } // We didn't get it, find next string to test // while ( *pattern && *pattern++ != EXIT_DELIMITER) { ; // Nothing. } while (mux_isspace(*pattern)) { pattern++; } } return false; } void string_token::set_source(UTF8* source_arg) { source = source_arg; } void string_token::set_control(const UTF8* control_arg) { memset(control, 0, sizeof(control)); // The '\0' character is always a control character. // control[0] = true; // Record the user-specified control characters. // while (*control_arg) { control[static_cast(*control_arg)] = 1; control_arg++; } } UTF8* string_token::parse_length(size_t* length) { *length = 0; UTF8 *p = source; if (!p) { return nullptr; } // Skip over leading control characters except for the NUL character. // while (control[static_cast(*p)] && *p) { p++; } UTF8 *pReturn = p; // Skip over non-control characters. // while (control[static_cast(*p)] == 0) { p++; } // What is the length of this token? // *length = p - pReturn; // Terminate the token with a NUL. // if (p[0]) { // We found a non-NUL delimiter, so the next call will begin parsing // on the character after this one. // source = p+1; } else { // We hit the end of the string, so the end of the string is where // the next call will begin. // source = p; } // Did we find a token? // if (*length > 0) { return pReturn; } else { return nullptr; } } UTF8* string_token::parse() { size_t nLen; UTF8 *p = parse_length(&nLen); if (p) { p[nLen] = '\0'; } return p; } mux_field StripTabsAndTruncate ( const UTF8 *pString, UTF8 *pBuffer, size_t nLength, size_t nWidth0 ) { mux_field fldOutput(0, 0); if ( nullptr == pBuffer || nullptr == pString || 0 == nLength || 0 == nWidth0 || '\0' == pString[0]) { if ( nullptr != pBuffer && 0 < nLength) { pBuffer[0] = '\0'; } return fldOutput; } LBUF_OFFSET nWidth = static_cast(nWidth0); if (nLength < nWidth) { nWidth = static_cast(nLength); } mux_cursor curPos(0, 0); mux_field fldLimit(nLength, nWidth); mux_field fldTransition(0, 0); mux_field fldNormal(0, 0); const UTF8 *pTransition = nullptr, *pNormal = nullptr; size_t nNormalBytes = 0, nTransition = 0; ColorState csCurrent = CS_NORMAL, csNext = CS_NORMAL; while ('\0' != pString[curPos.m_byte]) { int iCode = mux_color(pString + curPos.m_byte); size_t nPointBytes = utf8_FirstByte[static_cast(pString[curPos.m_byte])]; if (nPointBytes >= UTF8_CONTINUE) { nPointBytes = UTF8_SIZE1; } if (nPointBytes > nLength - curPos.m_byte) { nPointBytes = nLength - curPos.m_byte; } if (0 == nPointBytes) { break; } mux_cursor curPoint(static_cast(nPointBytes), 1); if (COLOR_NOTCOLOR != iCode) { csNext = UpdateColorState(csNext, iCode, pString + curPos.m_byte); } else if (nullptr == strchr("\r\n\t", pString[curPos.m_byte])) { int nPointWidth = ConsoleWidth(pString + curPos.m_byte); mux_field fldPoint(static_cast(nPointBytes), static_cast(nPointWidth)); if (csCurrent != csNext) { pTransition = ColorTransitionBinary(csCurrent, csNext, &nTransition); pNormal = ColorBinaryNormal(csNext, &nNormalBytes); fldNormal(nNormalBytes, 0); } else { nTransition = 0; } fldTransition(nTransition, 0); if (fldOutput + fldTransition + fldPoint + fldNormal <= fldLimit) { if (0 < nTransition) { memcpy(pBuffer + fldOutput.m_byte, pTransition, nTransition); csCurrent = csNext; } fldOutput += fldTransition; for (size_t j = 0; j < fldPoint.m_byte; j++) { pBuffer[fldOutput.m_byte + j] = pString[curPos.m_byte + j]; } fldOutput += fldPoint; } else { break; } } curPos += curPoint; } if (csCurrent != csNext) { pNormal = ColorBinaryNormal(csCurrent, &nNormalBytes); fldNormal(nNormalBytes, 0); } if ( 0 < nNormalBytes && fldOutput + fldNormal <= fldLimit) { memcpy(pBuffer + fldOutput.m_byte, pNormal, nNormalBytes); fldOutput += fldNormal; } pBuffer[fldOutput.m_byte] = '\0'; return fldOutput; } // TruncateToBuffer() // // pString is parsed into alternating runs of text and color. These runs are // then encoded into the given buffer with proper truncation. Every run of // text will be of the same color. The color is collapsed into the minimal // expression necessary to change from the color of the last run of text to // the color of the next run of text. The initial color state and last color // state are both CS_NORMAL. // // Parsing runs of text: As long as mux_color() returns COLOR_NOTCOLOR, we are // in a run of text. // // Parsing runs of color: As long as mux_color() returns something besides // COLOR_NOTCOLOR, we are in a run of color code points and use // UpdateColorState() to merge the color code point into the current color // state. // // It is sometimes not necessary to parse the entire string. Once we truncate // a run of text, we know that no runs of text after the truncated one will // fit either. // // The initial state is { CS_NORMAL, "" }. In turn, new runs of text, // { CS(i), TEXT(i) } are encoded. To encode { CS(i+1), TEXT(i+1) } on the // end of { CS(i), TEXT(i) }, we use ColorTransitionBinary(CS(i), CS(i+1), ...) // and ColorBinaryNormal(CS(i+1), ...) to determine the two transitions. If // there isn't enough room remaining in the buffer for these transitions, // TEXT(i+1), TEXT(i+1) is truncated until there is room. If TEXT(i+1) is // truncated to zero, neither color transition nor any of TEXT(i+1) is used. // size_t TruncateToBuffer ( const UTF8 *pString, UTF8 *pBuffer, size_t nBuffer ) { size_t nOutput = 0; if ( nullptr == pBuffer || nullptr == pString || 0 == nBuffer || '\0' == pString[0]) { if (nullptr != pBuffer) { pBuffer[0] = '\0'; } return nOutput; } size_t nNormal; const UTF8 *pNormal; ColorState csLast = CS_NORMAL; ColorState csCurrent = CS_NORMAL; bool bText = false; const UTF8 *p = pString; bool bTruncated = false; while ( '\0' != p[0] && !bTruncated) { // Parse a run of color code points. // int iCode; while ( ( UTF8_SIZE3 == utf8_FirstByte[p[0]] || UTF8_SIZE4 == utf8_FirstByte[p[0]]) && COLOR_NOTCOLOR != (iCode = mux_color(p))) { csCurrent = UpdateColorState(csCurrent, iCode, p); size_t nAdvance = utf8_advance_nul(p); if (0 == nAdvance) { break; } p += nAdvance; } // Parse a run of text. A run of text is always ended by '\0' and // sometimes by '\xEF' since all color code points start with '\xEF'. // bText = false; size_t nTextRun = 0; const UTF8 *pTextRun = p; for (;;) { const UTF8 *pEF, *pF3; if (nullptr != (pEF = reinterpret_cast(strchr(reinterpret_cast(p), '\xEF')))) { nTextRun += pEF - p; p = pEF; if (COLOR_NOTCOLOR != mux_color(p)) { break; } size_t nAdvance = utf8_advance_nul(p); nTextRun += nAdvance; p += nAdvance; } else if (nullptr != (pF3 = reinterpret_cast(strchr(reinterpret_cast(p), '\xF3')))) { nTextRun += pF3 - p; p = pF3; if (COLOR_NOTCOLOR != mux_color(p)) { break; } size_t nAdvance = utf8_advance_nul(p); nTextRun += nAdvance; p += nAdvance; } else { size_t n = strlen(reinterpret_cast(p)); nTextRun += n; p += n; break; } } // We have either reached a color code point or end of the string. // We have seen { csCurrent, (nTextRun, pTextRun) }. Before we parse // further, we need to encode this into the destination buffer. // There is a color transition, then a possibly truncated run of text // followed by another color transition to CS_NORMAL. We won't lay // anything down unless there is room for at least one character of // the text. // if (0 < nTextRun) { // Calculate the two transitions. // size_t nTransition; const UTF8 *pTransition = ColorTransitionBinary(csLast, csCurrent, &nTransition); pNormal = ColorBinaryNormal(csCurrent, &nNormal); size_t nMinChar = utf8_advance_nul(pTextRun); if (0 == nMinChar) { nMinChar = 1; } if (nOutput + nTransition + nMinChar + nNormal <= nBuffer) { // Lay down the initial color transition. // if (0 < nTransition) { memcpy(pBuffer + nOutput, pTransition, nTransition); nOutput += nTransition; csLast = csCurrent; } if ( nBuffer < nOutput + nTextRun + nNormal && nOutput + nNormal <= nBuffer) { // We need to truncate the text. // nTextRun = nBuffer - (nOutput + nNormal); while ( 0 < nTextRun && UTF8_CONTINUE <= utf8_FirstByte[pTextRun[nTextRun]]) { nTextRun--; } bTruncated = true; } // Lay down text. // bText = true; memcpy(pBuffer + nOutput, pTextRun, nTextRun); nOutput += nTextRun; // We have left room for the transition to CS_NORMAL, but it // isn't laid down. // } } } pNormal = ColorBinaryNormal((bText)?csCurrent:csLast, &nNormal); if ( 0 < nNormal && nOutput + nNormal <= nBuffer) { memcpy(pBuffer + nOutput, pNormal, nNormal); nOutput += nNormal; } pBuffer[nOutput] = '\0'; return nOutput; } mux_field PadField( UTF8 *pBuffer, size_t nMaxBytes, LBUF_OFFSET nMinWidth, mux_field fldOutput) { if (nullptr == pBuffer) { return fldMin; } while ( fldOutput.m_byte < nMaxBytes && fldOutput.m_column < nMinWidth) { pBuffer[fldOutput.m_byte] = static_cast(' '); fldOutput += fldAscii; } if (fldOutput.m_byte <= nMaxBytes) { pBuffer[fldOutput.m_byte] = '\0'; } return fldOutput; } void ItemToList_Init(ITL *p, UTF8 *arg_buff, UTF8 **arg_bufc, UTF8 arg_chPrefix, UTF8 arg_chSep) { p->bFirst = true; p->chPrefix = arg_chPrefix; p->chSep = arg_chSep; p->buff = arg_buff; p->bufc = arg_bufc; p->nBufferAvailable = LBUF_SIZE - (*arg_bufc - arg_buff) - 1; } bool ItemToList_AddInteger(ITL *pContext, int i) { UTF8 smbuf[SBUF_SIZE]; UTF8 *p = smbuf; if ( !pContext->bFirst && pContext->chSep) { *p++ = pContext->chSep; } if (pContext->chPrefix) { *p++ = pContext->chPrefix; } p += mux_ltoa(i, p); size_t nLen = p - smbuf; if ( pContext->nBufferAvailable < nLen || sizeof(smbuf) < nLen) { // Out of room. // return false; } if (pContext->bFirst) { pContext->bFirst = false; } memcpy(*(pContext->bufc), smbuf, nLen); *(pContext->bufc) += nLen; pContext->nBufferAvailable -= nLen; return true; } bool ItemToList_AddInteger64(ITL *pContext, int64_t i64) { UTF8 smbuf[SBUF_SIZE]; UTF8 *p = smbuf; if ( !pContext->bFirst && pContext->chSep) { *p++ = pContext->chSep; } if (pContext->chPrefix) { *p++ = pContext->chPrefix; } p += mux_i64toa(i64, p); size_t nLen = p - smbuf; if ( pContext->nBufferAvailable < nLen || sizeof(smbuf) < nLen) { // Out of room. // return false; } if (pContext->bFirst) { pContext->bFirst = false; } memcpy(*(pContext->bufc), smbuf, nLen); *(pContext->bufc) += nLen; pContext->nBufferAvailable -= nLen; return true; } bool ItemToList_AddStringLEN(ITL *pContext, size_t nStr, const UTF8 *pStr) { size_t nLen = nStr; if ( !pContext->bFirst && pContext->chSep) { nLen++; } if (pContext->chPrefix) { nLen++; } if (nLen > pContext->nBufferAvailable) { // Out of room. // return false; } UTF8 *p = *(pContext->bufc); if (pContext->bFirst) { pContext->bFirst = false; } else if (pContext->chSep) { *p++ = pContext->chSep; } if (pContext->chPrefix) { *p++ = pContext->chPrefix; } memcpy(p, pStr, nStr); *(pContext->bufc) += nLen; pContext->nBufferAvailable -= nLen; return true; } bool ItemToList_AddString(ITL *pContext, const UTF8 *pStr) { size_t nStr = strlen(reinterpret_cast(pStr)); return ItemToList_AddStringLEN(pContext, nStr, pStr); } void ItemToList_Final(ITL *pContext) { **(pContext->bufc) = '\0'; } // mux_stricmp - Compare two strings ignoring case. // int mux_stricmp(const UTF8 *a, const UTF8 *b) { // ASCII fast path: compare via tolower table without entering // the Unicode state machine. Falls through to the full path // when a non-ASCII byte is encountered. // while (*a && *b && *a < 0x80 && *b < 0x80) { unsigned char la = mux_tolower_ascii[*a]; unsigned char lb = mux_tolower_ascii[*b]; if (la != lb) { return (la < lb) ? -1 : 1; } a++; b++; } for (;;) { if ('\0' == *a || '\0' == *b) { // Compare terminating bytes. // if (*a < *b) return -1; if (*a > *b) return 1; return 0; } // Get lowercased bytes for each code point. // UTF8 la[4], lb[4]; size_t na, nb; bool bXorA; const string_desc *qA = mux_tolower(a, bXorA); if (nullptr == qA) { na = utf8_FirstByte[static_cast(*a)]; if (na >= UTF8_CONTINUE) na = 1; for (size_t j = 0; j < na; j++) la[j] = a[j]; } else { na = qA->n_bytes; if (bXorA) { for (size_t j = 0; j < na; j++) la[j] = a[j] ^ qA->p[j]; } else { for (size_t j = 0; j < na; j++) la[j] = qA->p[j]; } } bool bXorB; const string_desc *qB = mux_tolower(b, bXorB); if (nullptr == qB) { nb = utf8_FirstByte[static_cast(*b)]; if (nb >= UTF8_CONTINUE) nb = 1; for (size_t j = 0; j < nb; j++) lb[j] = b[j]; } else { nb = qB->n_bytes; if (bXorB) { for (size_t j = 0; j < nb; j++) lb[j] = b[j] ^ qB->p[j]; } else { for (size_t j = 0; j < nb; j++) lb[j] = qB->p[j]; } } // Compare the lowercased code-point bytes. // size_t nMin = (na < nb) ? na : nb; for (size_t j = 0; j < nMin; j++) { if (la[j] < lb[j]) return -1; if (la[j] > lb[j]) return 1; } if (na < nb) return -1; if (na > nb) return 1; a += na; b += nb; } } // mux_memicmp - Compare two buffers ignoring case (Unicode-aware). // Bounded by byte count n from each buffer. // int mux_memicmp(const void *p1_arg, const void *p2_arg, size_t n) { const UTF8 *p1 = reinterpret_cast(p1_arg); const UTF8 *p2 = reinterpret_cast(p2_arg); const UTF8 *p1End = p1 + n; const UTF8 *p2End = p2 + n; while (p1 < p1End && p2 < p2End) { UTF8 la[4], lb[4]; size_t na, nb; size_t remA = static_cast(p1End - p1); size_t srcA = utf8_FirstByte[static_cast(*p1)]; if (srcA >= UTF8_CONTINUE) { srcA = UTF8_SIZE1; } bool bValidA = (srcA <= remA); if (bValidA) { for (size_t i = 1; i < srcA; i++) { if (UTF8_CONTINUE != utf8_FirstByte[static_cast(p1[i])]) { bValidA = false; break; } } } if (!bValidA) { srcA = UTF8_SIZE1; } UTF8 cpA[5]; for (size_t j = 0; j < srcA; j++) { cpA[j] = p1[j]; } cpA[srcA] = '\0'; bool bXorA; const string_desc *qA = bValidA ? mux_tolower(cpA, bXorA) : nullptr; if (nullptr == qA) { na = srcA; for (size_t j = 0; j < na; j++) la[j] = cpA[j]; } else { na = qA->n_bytes; if (bXorA && na > srcA) { na = srcA; } if (na > remA) { na = remA; } if (bXorA) { for (size_t j = 0; j < na; j++) la[j] = cpA[j] ^ qA->p[j]; } else { for (size_t j = 0; j < na; j++) la[j] = qA->p[j]; } } size_t remB = static_cast(p2End - p2); size_t srcB = utf8_FirstByte[static_cast(*p2)]; if (srcB >= UTF8_CONTINUE) { srcB = UTF8_SIZE1; } bool bValidB = (srcB <= remB); if (bValidB) { for (size_t i = 1; i < srcB; i++) { if (UTF8_CONTINUE != utf8_FirstByte[static_cast(p2[i])]) { bValidB = false; break; } } } if (!bValidB) { srcB = UTF8_SIZE1; } UTF8 cpB[5]; for (size_t j = 0; j < srcB; j++) { cpB[j] = p2[j]; } cpB[srcB] = '\0'; bool bXorB; const string_desc *qB = bValidB ? mux_tolower(cpB, bXorB) : nullptr; if (nullptr == qB) { nb = srcB; for (size_t j = 0; j < nb; j++) lb[j] = cpB[j]; } else { nb = qB->n_bytes; if (bXorB && nb > srcB) { nb = srcB; } if (nb > remB) { nb = remB; } if (bXorB) { for (size_t j = 0; j < nb; j++) lb[j] = cpB[j] ^ qB->p[j]; } else { for (size_t j = 0; j < nb; j++) lb[j] = qB->p[j]; } } size_t nMin = (na < nb) ? na : nb; for (size_t j = 0; j < nMin; j++) { if (la[j] < lb[j]) return -1; if (la[j] > lb[j]) return 1; } if (na < nb) return -1; if (na > nb) return 1; p1 += na; p2 += nb; } if (p1 < p1End) return 1; if (p2 < p2End) return -1; return 0; } // mux_strlwr - Convert string to all lower case. // UTF8 *mux_strlwr(const UTF8 *a, size_t &n) { static UTF8 Buffer[LBUF_SIZE]; n = 0; while ('\0' != *a) { size_t j; size_t src = utf8_FirstByte[static_cast(*a)]; if (src >= UTF8_CONTINUE) { src = UTF8_SIZE1; } else { for (j = 1; j < src; j++) { if ( '\0' == a[j] || UTF8_CONTINUE != utf8_FirstByte[static_cast(a[j])]) { src = UTF8_SIZE1; break; } } } size_t out = src; bool bXor; const string_desc *qDesc = mux_tolower(a, bXor); if (nullptr == qDesc) { if (LBUF_SIZE-1 < n + out) { break; } for (j = 0; j < out; j++) { Buffer[n+j] = a[j]; } } else { out = qDesc->n_bytes; if ( bXor && out != src) { out = src; qDesc = nullptr; bXor = false; } if (LBUF_SIZE-1 < n + out) { break; } if (nullptr == qDesc) { for (j = 0; j < out; j++) { Buffer[n+j] = a[j]; } } else if (bXor) { for (j = 0; j < out; j++) { Buffer[n+j] = a[j] ^ qDesc->p[j]; } } else { for (j = 0; j < out; j++) { Buffer[n+j] = qDesc->p[j]; } } } n += out; a += src; } Buffer[n] = '\0'; return Buffer; } // mux_strupr - Convert string to all upper case. // UTF8 *mux_strupr(const UTF8 *a, size_t &n) { static UTF8 Buffer[LBUF_SIZE]; n = 0; while ('\0' != *a) { size_t j; size_t src = utf8_FirstByte[static_cast(*a)]; if (src >= UTF8_CONTINUE) { src = UTF8_SIZE1; } else { for (j = 1; j < src; j++) { if ( '\0' == a[j] || UTF8_CONTINUE != utf8_FirstByte[static_cast(a[j])]) { src = UTF8_SIZE1; break; } } } size_t out = src; bool bXor; const string_desc *qDesc = mux_toupper(a, bXor); if (nullptr == qDesc) { if (LBUF_SIZE-1 < n + out) { break; } for (j = 0; j < out; j++) { Buffer[n+j] = a[j]; } } else { out = qDesc->n_bytes; if ( bXor && out != src) { out = src; qDesc = nullptr; bXor = false; } if (LBUF_SIZE-1 < n + out) { break; } if (nullptr == qDesc) { for (j = 0; j < out; j++) { Buffer[n+j] = a[j]; } } else if (bXor) { for (j = 0; j < out; j++) { Buffer[n+j] = a[j] ^ qDesc->p[j]; } } else { for (j = 0; j < out; j++) { Buffer[n+j] = qDesc->p[j]; } } } n += out; a += src; } Buffer[n] = '\0'; return Buffer; } // mux_toupper_first - Uppercase the first UTF-8 code point in-place in a // buffer. buff points to the first byte to transform, bufc points to the // current end-of-data pointer, nBufferTotal is the total buffer size. // // For XOR transforms (same byte length), overwrites in place. // For literal transforms (may change byte length), shifts the remaining // buffer contents via memmove and adjusts *bufc. // void mux_toupper_first(UTF8 *buff, UTF8 **bufc, size_t nBufferTotal) { if ( nullptr == buff || nullptr == bufc || nullptr == *bufc || nBufferTotal < 1 || buff >= *bufc || '\0' == *buff) { return; } size_t nOld = utf8_FirstByte[static_cast(*buff)]; if (nOld >= UTF8_CONTINUE) { return; } // Validate that we have enough bytes. // size_t nUsed = *bufc - buff; if (nOld > nUsed) { return; } for (size_t j = 1; j < nOld; j++) { if ( j >= nUsed || UTF8_CONTINUE != utf8_FirstByte[static_cast(buff[j])]) { return; } } bool bXor; const string_desc *qDesc = mux_toupper(buff, bXor); if (nullptr == qDesc) { // No transform for this code point — already uppercase or not a letter. // return; } size_t nNew = qDesc->n_bytes; if (bXor) { // XOR transform: same byte length, overwrite in place. // if ( nNew != nOld || nNew > nUsed) { return; } for (size_t j = 0; j < nNew; j++) { buff[j] ^= qDesc->p[j]; } } else { // Literal transform: byte length may differ. // size_t nTail = nUsed - nOld; // Check that the result fits in the buffer. // size_t nBufferEnd = nBufferTotal - 1; size_t nBuffStart = buff - (*bufc - nUsed); // offset of buff from buffer start if (nBuffStart + nNew + nTail > nBufferEnd) { // Would overflow — truncate by not transforming. // return; } // Shift the tail to make room (or shrink). // if (nNew != nOld) { memmove(buff + nNew, buff + nOld, nTail + 1); // +1 for NUL *bufc += (nNew - nOld); } // Write the transformed bytes. // for (size_t j = 0; j < nNew; j++) { buff[j] = qDesc->p[j]; } } } // mux_foldmatch - alias for matching. // UTF8 *mux_foldmatch(const UTF8 *a, size_t &n, bool &fChanged) { static UTF8 Buffer[LBUF_SIZE]; n = 0; fChanged = false; while ('\0' != *a) { size_t j; size_t src = utf8_FirstByte[static_cast(*a)]; if (src >= UTF8_CONTINUE) { src = UTF8_SIZE1; } else { for (j = 1; j < src; j++) { if ( '\0' == a[j] || UTF8_CONTINUE != utf8_FirstByte[static_cast(a[j])]) { src = UTF8_SIZE1; break; } } } size_t out = src; bool bXor; const string_desc *qDesc = mux_foldmatch(a, bXor); if (nullptr == qDesc) { if (LBUF_SIZE-1 < n + out) { break; } for (j = 0; j < out; j++) { Buffer[n+j] = a[j]; } } else { out = qDesc->n_bytes; if ( bXor && out != src) { out = src; qDesc = nullptr; bXor = false; } if (LBUF_SIZE-1 < n + out) { break; } if (nullptr == qDesc) { for (j = 0; j < out; j++) { Buffer[n+j] = a[j]; } } else if (bXor) { for (j = 0; j < out; j++) { Buffer[n+j] = a[j] ^ qDesc->p[j]; } } else { for (j = 0; j < out; j++) { Buffer[n+j] = qDesc->p[j]; } } fChanged = true; } n += out; a += src; } Buffer[n] = '\0'; return Buffer; } // Render a double in printf's %f / %e / %g shapes using David M. Gay's dtoa // for the digits. // // Not snprintf: this keeps float rendering on the same correctly-rounded // digit generator the rest of the tree already uses (mux_ftoa, fval, // NearestPretty all go through mux_dtoa), rather than making output depend on // the host libc. mux_ftoa itself is not usable here -- it is MUX's own float // rendering and switches to exponent form once the decimal point passes 18, // which %f never does. // // dtoa gives digits and a decimal-point position with trailing zeros // suppressed, so the padding back out to the requested precision is ours. // decpt == 9999 signals Inf/NaN. // // Returns the length written, or 0 with *pbOverflow set when the result will // not fit nScratch. The caller treats that as truncation; before %f was // implemented at all this path was a mux_assert(0), so truncating is strictly // an improvement on aborting the process. // #define MUX_FLOAT_SCRATCH 512 static size_t mux_format_double(UTF8 *pScratch, size_t nScratch, double dval, UTF8 chConv, int nPrec, bool *pbOverflow) { *pbOverflow = false; bool bUpper = ( 'F' == chConv || 'E' == chConv || 'G' == chConv); UTF8 chLower = static_cast(bUpper ? chConv - 'A' + 'a' : chConv); int decpt = 0; int bNeg = 0; UTF8 *rve = nullptr; UTF8 *pDigits = nullptr; // %g selects between %e and %f on the exponent, so it has to see the // significant digits before it can choose. // int nSig = 0; if ('g' == chLower) { nSig = (0 == nPrec) ? 1 : nPrec; pDigits = mux_dtoa(dval, 2, nSig, &decpt, &bNeg, &rve); } else if ('e' == chLower) { pDigits = mux_dtoa(dval, 2, nPrec + 1, &decpt, &bNeg, &rve); } else { pDigits = mux_dtoa(dval, 3, nPrec, &decpt, &bNeg, &rve); } if (nullptr == pDigits) { *pbOverflow = true; return 0; } size_t nDigits = static_cast(rve - pDigits); size_t q = 0; // Inf and NaN. printf spells these "inf"/"nan", uppercased for the // uppercase conversions; a sign is carried on infinity only. // if (9999 == decpt) { const char *pWord = ('n' == pDigits[0] || 'N' == pDigits[0]) ? "nan" : "inf"; if ( bNeg && 'i' == pWord[0]) { pScratch[q++] = '-'; } for (int i = 0; i < 3; i++) { pScratch[q++] = static_cast(bUpper ? pWord[i] - 'a' + 'A' : pWord[i]); } pScratch[q] = '\0'; return q; } // dtoa returns nothing for zero. Treat it as a single '0' digit sitting // just left of the point, which is what the assembly below expects. // UTF8 chZero = '0'; if (0 == nDigits) { pDigits = &chZero; nDigits = 1; decpt = 1; } // Resolve %g to whichever of %e / %f it renders as. C's rule: use %e // when the exponent is below -4 or at least the precision. // bool bStripTrailing = false; int nExp = decpt - 1; if ('g' == chLower) { bStripTrailing = true; if ( nExp < -4 || nSig <= nExp) { chLower = 'e'; nPrec = nSig - 1; } else { chLower = 'f'; nPrec = nSig - 1 - nExp; if (nPrec < 0) { nPrec = 0; } } } // Worst case is %f of DBL_MAX: ~309 integer digits, plus the point, plus // the precision. Bail rather than overrun. // size_t nWorst = (('f' == chLower) ? (decpt > 0 ? static_cast(decpt) : 1) : 1) + static_cast(nPrec) + 16; if (nScratch <= nWorst) { *pbOverflow = true; return 0; } if (bNeg) { pScratch[q++] = '-'; } // %g suppresses trailing zeros, but only inside the fraction: the strip // must never reach back into the integer digits, or "%.1g" of 0 renders // as the empty string instead of "0". iDot records where the fraction // begins; bDot says whether there is one at all. // size_t iDot = 0; bool bDot = false; if ('e' == chLower) { pScratch[q++] = pDigits[0]; if (0 < nPrec) { iDot = q; bDot = true; pScratch[q++] = '.'; for (int i = 0; i < nPrec; i++) { size_t k = static_cast(i) + 1; pScratch[q++] = (k < nDigits) ? pDigits[k] : '0'; } } if ( bStripTrailing && bDot) { while ( iDot + 1 < q && '0' == pScratch[q-1]) q--; if (iDot + 1 == q) q = iDot; } pScratch[q++] = static_cast(bUpper ? 'E' : 'e'); int nShow = nExp; pScratch[q++] = static_cast((nShow < 0) ? '-' : '+'); if (nShow < 0) nShow = -nShow; if (nShow < 10) { pScratch[q++] = '0'; pScratch[q++] = static_cast('0' + nShow); } else { q += mux_ltoa(nShow, pScratch + q); } } else { // Integer part. // if (decpt <= 0) { pScratch[q++] = '0'; } else { for (int i = 0; i < decpt; i++) { size_t k = static_cast(i); pScratch[q++] = (k < nDigits) ? pDigits[k] : '0'; } } if (0 < nPrec) { iDot = q; bDot = true; pScratch[q++] = '.'; for (int i = 0; i < nPrec; i++) { int k = decpt + i; pScratch[q++] = ( 0 <= k && static_cast(k) < nDigits) ? pDigits[static_cast(k)] : '0'; } } if ( bStripTrailing && bDot) { while ( iDot + 1 < q && '0' == pScratch[q-1]) q--; if (iDot + 1 == q) q = iDot; } } pScratch[q] = '\0'; return q; } // --------------------------------------------------------------------------- // POSIX %N$ positional arguments for mux_vsnprintf (#1623). // // gettext catalogues reorder multi-conversion msgids with %1$s / %2$d. The // sequential path below cannot resynchronise a va_list cursor, so positional // formats are handled by: (1) scanning every conversion for its 1-based index // and type, (2) pulling arguments 1..max from va_list in index order, (3) // formatting with those saved values. The sequential path is unchanged when // the format has no %N$. // // Mix of positional and non-positional conversions (other than %%) is rejected // with the #1429 stop policy. // --------------------------------------------------------------------------- #define MUX_PRINTF_MAX_ARGS 32 enum mux_printf_akind { MPA_NONE = 0, MPA_INT, MPA_LONG, MPA_I64, MPA_UINT, MPA_ULONG, MPA_U64, MPA_STR, MPA_PTR, MPA_DOUBLE, MPA_CHAR }; struct mux_printf_arg { mux_printf_akind kind; union { int i; long l; int64_t i64; unsigned int u; unsigned long ul; uint64_t u64; UTF8 *s; void *p; double d; unsigned int c; } v; }; // True if pFmt contains at least one %N$ conversion (N >= 1). // static bool mux_fmt_has_positional(const UTF8 *pFmt) { if (nullptr == pFmt) { return false; } for (const UTF8 *p = pFmt; '\0' != *p; p++) { if ('%' != *p) { continue; } p++; if ('\0' == *p) { break; } if ('%' == *p) { continue; } // Position is the first field after '%': digits then '$'. // if ( *p < '1' || *p > '9') { continue; } while ( *p >= '0' && *p <= '9') { p++; } if ('$' == *p) { return true; } if ('\0' == *p) { break; } } return false; } // Map a conversion letter + nLongs to an argument kind. // static mux_printf_akind mux_fmt_arg_kind(UTF8 ch, int nLongs) { switch (ch) { case 'd': case 'i': if (0 == nLongs) { return MPA_INT; } if (1 == nLongs) { return MPA_LONG; } if (2 == nLongs) { return MPA_I64; } return MPA_NONE; case 'u': case 'o': case 'x': case 'X': if (0 == nLongs) { return MPA_UINT; } if (1 == nLongs) { return MPA_ULONG; } if (2 == nLongs) { return MPA_U64; } return MPA_NONE; case 's': return (0 == nLongs) ? MPA_STR : MPA_NONE; case 'p': return (0 == nLongs) ? MPA_PTR : MPA_NONE; case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': return MPA_DOUBLE; case 'c': return MPA_CHAR; default: return MPA_NONE; } } // Scan pFmt for every conversion: record kind[pos] (1-based). *pMaxPos is // the highest index referenced. Returns false on mix of positional and // sequential, type conflict, or an unparseable conversion. // static bool mux_fmt_scan_kinds( const UTF8 *pFmt, mux_printf_akind *kinds, int *pMaxPos) { for (int i = 0; i <= MUX_PRINTF_MAX_ARGS; i++) { kinds[i] = MPA_NONE; } *pMaxPos = 0; bool bAnyPos = false; bool bAnySeq = false; int nNextSeq = 1; size_t ncpFmt = 0; if ( nullptr == pFmt || !utf8_strlen(pFmt, ncpFmt)) { return true; } size_t iFmt = 0; while (0 != ncpFmt) { if ('%' != pFmt[iFmt]) { size_t d = utf8_FirstByte[pFmt[iFmt]]; iFmt += d; ncpFmt--; continue; } iFmt++; ncpFmt--; if (0 == ncpFmt) { return false; } if ('%' == pFmt[iFmt]) { iFmt++; ncpFmt--; continue; } int nPos = 0; int nLongs = 0; // Optional N$ // if ( pFmt[iFmt] >= '1' && pFmt[iFmt] <= '9') { size_t j = iFmt; size_t n = 0; size_t nDig = 0; while ( nDig < ncpFmt && pFmt[j] >= '0' && pFmt[j] <= '9') { n = 10 * n + static_cast(pFmt[j] - '0'); j++; nDig++; } if ( nDig < ncpFmt && '$' == pFmt[j] && n > 0 && n <= MUX_PRINTF_MAX_ARGS) { nPos = static_cast(n); iFmt = j + 1; ncpFmt -= nDig + 1; bAnyPos = true; } } if (0 == nPos) { nPos = nNextSeq++; bAnySeq = true; } if ( bAnyPos && bAnySeq) { return false; } // Skip flags / width / precision / length to the type letter. // while (0 != ncpFmt) { UTF8 ch = pFmt[iFmt]; if ( 'd' == ch || 'i' == ch || 's' == ch || 'u' == ch || 'o' == ch || 'x' == ch || 'X' == ch || 'p' == ch || 'f' == ch || 'F' == ch || 'e' == ch || 'E' == ch || 'g' == ch || 'G' == ch || 'c' == ch) { mux_printf_akind k = mux_fmt_arg_kind(ch, nLongs); if (MPA_NONE == k) { return false; } if ( MPA_NONE != kinds[nPos] && kinds[nPos] != k) { return false; } kinds[nPos] = k; if (nPos > *pMaxPos) { *pMaxPos = nPos; } iFmt++; ncpFmt--; break; } else if ('l' == ch) { nLongs++; iFmt++; ncpFmt--; } else if ('z' == ch) { nLongs = (sizeof(size_t) == sizeof(unsigned long)) ? 1 : 2; iFmt++; ncpFmt--; } else if ( ('0' <= ch && ch <= '9') || '.' == ch || '-' == ch || '+' == ch || ' ' == ch || '#' == ch || '*' == ch) { // Width, precision, flags — including '*' which we do not // support as a value but must skip past if present. // iFmt++; ncpFmt--; } else { return false; } } } return true; } static bool mux_fmt_fill_args( mux_printf_akind *kinds, int nMaxPos, va_list va, mux_printf_arg *args) { for (int i = 1; i <= nMaxPos; i++) { args[i].kind = kinds[i]; switch (kinds[i]) { case MPA_INT: args[i].v.i = va_arg(va, int); break; case MPA_LONG: args[i].v.l = va_arg(va, long); break; case MPA_I64: args[i].v.i64 = va_arg(va, int64_t); break; case MPA_UINT: args[i].v.u = va_arg(va, unsigned int); break; case MPA_ULONG: args[i].v.ul = va_arg(va, unsigned long); break; case MPA_U64: args[i].v.u64 = va_arg(va, uint64_t); break; case MPA_STR: args[i].v.s = va_arg(va, UTF8 *); break; case MPA_PTR: args[i].v.p = va_arg(va, void *); break; case MPA_DOUBLE: args[i].v.d = va_arg(va, double); break; case MPA_CHAR: args[i].v.c = va_arg(va, unsigned int); break; case MPA_NONE: // Gap in the position list — leave zeroed; a later conversion // that references it will fail closed. // break; default: return false; } } return true; } // mux_vsnprintf - Is an sprintf-like function that will not overflow // a buffer of specific size. The size is give by count, and count // should be chosen to include the '\0' termination. // // Returns: A number from 0 to count-1 that is the string length of // the returned (possibly truncated) buffer. // // Supports POSIX %N$ positional conversions (#1623) when the format uses // them; sequential formats keep the original single-pass path. // size_t DCL_CDECL mux_vsnprintf(UTF8 *pBuffer, size_t nBuffer, const UTF8 *pFmt, va_list va) { if ( nullptr == pBuffer || nBuffer < 1) { return 0; } size_t nLimit = nBuffer-1; // Positional mode: materialise args, then format with saved values. // mux_printf_arg posArgs[MUX_PRINTF_MAX_ARGS + 1]; bool bPositional = false; int nMaxPos = 0; memset(posArgs, 0, sizeof(posArgs)); if ( nullptr != pFmt && mux_fmt_has_positional(pFmt)) { mux_printf_akind kinds[MUX_PRINTF_MAX_ARGS + 1]; if ( !mux_fmt_scan_kinds(pFmt, kinds, &nMaxPos) || nMaxPos < 1 || nMaxPos > MUX_PRINTF_MAX_ARGS || !mux_fmt_fill_args(kinds, nMaxPos, va, posArgs)) { // Unparseable or mixed positional/sequential — #1429 stop: // emit nothing beyond an empty string rather than walk va_list // wrong. Callers already tolerate truncation. // pBuffer[0] = '\0'; return 0; } bPositional = true; } // Rather than copy a character at a time, some copies are deferred and performed in a single request. // size_t iFmtDeferred = 0; size_t dDeferred = 0; size_t iBuffer = 0; size_t ncpFmt; size_t iFmt = 0; int nNextSeq = 1; if ( nullptr != pFmt && utf8_strlen(pFmt, ncpFmt)) { // Octal needs 22 digits for a 64-bit value plus the terminator, one // more than I64BUF_SIZE allows for decimal. Floating point does not // use this buffer at all -- see the 'f'/'e'/'g' branch, which formats // straight into the output. // static UTF8 Buff[I64BUF_SIZE + 8]; while (0 != ncpFmt) { if ('%' != pFmt[iFmt]) { // Ordinary character. // size_t d = utf8_FirstByte[pFmt[iFmt]]; size_t dProposed = dDeferred + d; if (nLimit < iBuffer + dProposed) { if (0 < dDeferred) { // Unravel the deferred copy. // memcpy(pBuffer + iBuffer, pFmt + iFmtDeferred, dDeferred); iBuffer += dDeferred; dDeferred = 0; } goto done; } else if (0 == dDeferred) { iFmtDeferred = iFmt; } dDeferred = dProposed; iFmt += d; ncpFmt--; } else { if (0 < dDeferred) { // Unravel the deferred copy. // memcpy(pBuffer + iBuffer, pFmt + iFmtDeferred, dDeferred); iBuffer += dDeferred; dDeferred = 0; } size_t cbBuff; size_t cpBuff; size_t nWidth = 0; size_t nPrecision = 0; bool bLeft = false; bool bZeroPadded = false; bool bWidth = false; bool bSawPeriod = false; bool bPrecision = false; int nLongs = 0; int nArgPos = 0; // Where this conversion spec starts, so an unimplemented one // can be echoed literally rather than killing the process // (#1429). // size_t iFmtSpec = iFmt; iFmt++; ncpFmt--; // Optional POSIX position N$ immediately after '%' (#1623). // if ( bPositional && 0 != ncpFmt && pFmt[iFmt] >= '1' && pFmt[iFmt] <= '9') { size_t j = iFmt; size_t n = 0; size_t nDig = 0; while ( nDig < ncpFmt && pFmt[j] >= '0' && pFmt[j] <= '9') { n = 10 * n + static_cast(pFmt[j] - '0'); j++; nDig++; } if ( nDig < ncpFmt && '$' == pFmt[j] && n > 0 && n <= MUX_PRINTF_MAX_ARGS) { nArgPos = static_cast(n); iFmt = j + 1; ncpFmt -= nDig + 1; } } while (0 != ncpFmt) { if ( 'd' == pFmt[iFmt] || 'i' == pFmt[iFmt] || 's' == pFmt[iFmt] || 'u' == pFmt[iFmt] || 'o' == pFmt[iFmt] || 'x' == pFmt[iFmt] || 'X' == pFmt[iFmt] || 'p' == pFmt[iFmt] || 'f' == pFmt[iFmt] || 'F' == pFmt[iFmt] || 'e' == pFmt[iFmt] || 'E' == pFmt[iFmt] || 'g' == pFmt[iFmt] || 'G' == pFmt[iFmt]) { UTF8 *pBuff = Buff; UTF8 FBuff[MUX_FLOAT_SCRATCH]; // Resolve which saved arg (positional) or va_arg // (sequential) to consume. // const mux_printf_arg *pArg = nullptr; if (bPositional) { if (0 == nArgPos) { nArgPos = nNextSeq++; } if ( nArgPos < 1 || nArgPos > nMaxPos || MPA_NONE == posArgs[nArgPos].kind) { goto done; } pArg = &posArgs[nArgPos]; } if ( 'd' == pFmt[iFmt] || 'i' == pFmt[iFmt]) { // Obtain and validate argument. // if (0 == nLongs) { int i = (nullptr != pArg) ? pArg->v.i : va_arg(va, int); cbBuff = cpBuff = mux_ltoa(i, Buff); } else if (1 == nLongs) { long int i = (nullptr != pArg) ? pArg->v.l : va_arg(va, long int); cbBuff = cpBuff = mux_ltoa(i, Buff); } else if (2 == nLongs) { int64_t i = (nullptr != pArg) ? pArg->v.i64 : va_arg(va, int64_t); cbBuff = cpBuff = mux_i64toa(i, Buff); } else { goto done; } } else if ('s' == pFmt[iFmt]) { // Obtain and validate argument. // pBuff = (nullptr != pArg) ? pArg->v.s : va_arg(va, UTF8 *); if ( !utf8_strlen(pBuff, cpBuff) || 0 != nLongs) { goto done; } cbBuff = strlen(reinterpret_cast(pBuff)); if ( bPrecision && nPrecision < cpBuff) { // Need to walk cbBuff back to correspond to changes in cpBuff. // while (cpBuff != nPrecision) { do { cbBuff--; } while (UTF8_CONTINUE <= utf8_FirstByte[pBuff[cbBuff]]); cpBuff--; } } } else if ('p' == pFmt[iFmt]) { if ( 0 != nLongs || bWidth) { goto done; } // Convert pointer to unsigned integer. // union { uintptr_t ui; void *pv; } u; u.pv = (nullptr != pArg) ? pArg->v.p : va_arg(va, void *); #if SIZEOF_UINT_PTR <= SIZEOF_UNSIGNED_LONG cbBuff = cpBuff = mux_utox(u.ui, Buff, true); #elif SIZEOF_UINT_PTR <= SIZEOF_UNSIGNED_LONG_LONG cbBuff = cpBuff = mux_ui64tox(u.ui, Buff, true); #else #error Size of pointer is larger size of largest known integer. #endif bWidth = true; nWidth = 2*sizeof(uintptr_t); bZeroPadded = true; } else if ( 'f' == pFmt[iFmt] || 'F' == pFmt[iFmt] || 'e' == pFmt[iFmt] || 'E' == pFmt[iFmt] || 'g' == pFmt[iFmt] || 'G' == pFmt[iFmt]) { // Floating point. Before this, %f and friends // fell through to the mux_assert(0) below and took // the process down -- a standard C conversion this // function happened not to implement was a server // abort with no warning at the call site (#1382, // and again in @list). // // Digits come from mux_dtoa, the same correctly // rounded generator mux_ftoa/fval/NearestPretty // use, so float output does not depend on the host // libc. nLongs is ignored: "%lf" is double in // printf, and "%Lf" never reaches here because 'L' // is not parsed as a length modifier. // double dval = (nullptr != pArg) ? pArg->v.d : va_arg(va, double); int nPrec = bSawPeriod ? static_cast(nPrecision) : 6; bool bOverflow = false; cbBuff = cpBuff = mux_format_double(FBuff, sizeof(FBuff), dval, pFmt[iFmt], nPrec, &bOverflow); if (bOverflow) { goto done; } pBuff = FBuff; } else { bool bHex = ( 'x' == pFmt[iFmt] || 'X' == pFmt[iFmt]); bool bOct = ('o' == pFmt[iFmt]); bool bUpper = ('X' == pFmt[iFmt]); // Obtain and validate argument. // if (0 == nLongs) { unsigned int ui = (nullptr != pArg) ? pArg->v.u : va_arg(va, unsigned int); cbBuff = cpBuff = bOct?mux_utoo(ui, Buff):(bHex?mux_utox(ui, Buff, bUpper):mux_utoa(ui, Buff)); } else if (1 == nLongs) { unsigned long int ui = (nullptr != pArg) ? pArg->v.ul : va_arg(va, unsigned long int); cbBuff = cpBuff = bOct?mux_utoo(ui, Buff):(bHex?mux_utox(ui, Buff, bUpper):mux_utoa(ui, Buff)); } else if (2 == nLongs) { uint64_t ui = (nullptr != pArg) ? pArg->v.u64 : va_arg(va, uint64_t); cbBuff = cpBuff = bOct?mux_ui64too(ui, Buff):(bHex?mux_ui64tox(ui, Buff, bUpper):mux_ui64toa(ui, Buff)); } else { goto done; } } // Calculate and validate needed size. Numberic and // string fields are at least the size of their width. // String fields may have been truncated above by // precision. // // Width is compared with the number of code points. // Padding is always done with space or zero. // size_t nUsed = cbBuff; size_t nPadding = 0; if ( bWidth && cpBuff < nWidth) { nPadding = nWidth - cpBuff; nUsed += nPadding; } if (nLimit < iBuffer + nUsed) { goto done; } // Apply leading padding if necessary. // if ( !bLeft && bWidth) { // Any signed conversion: the minus sign has to be // laid down before zero-padding begins, or // "%08.2f" of -3.5 renders as "0000-3.50". // if ( ( 'd' == pFmt[iFmt] || 'i' == pFmt[iFmt] || 'f' == pFmt[iFmt] || 'F' == pFmt[iFmt] || 'e' == pFmt[iFmt] || 'E' == pFmt[iFmt] || 'g' == pFmt[iFmt] || 'G' == pFmt[iFmt]) && '-' == pBuff[0] && 0 < nPadding && bZeroPadded) { // The leading minus sign must be laid down before zero-padding begins. // pBuffer[iBuffer] = '-'; iBuffer++; pBuff++; cbBuff--; cpBuff--; } while (0 < nPadding) { pBuffer[iBuffer] = bZeroPadded?'0':' '; iBuffer++; nPadding--; } } // Apply string. // memcpy(pBuffer + iBuffer, pBuff, cbBuff); iBuffer += cbBuff; // Apply trailing padding if necessary. // if ( bLeft && bWidth) { while (0 < nPadding) { pBuffer[iBuffer] = bZeroPadded?'0':' '; iBuffer++; nPadding--; } } iFmt++; ncpFmt--; break; } else if ('l' == pFmt[iFmt]) { nLongs++; iFmt++; ncpFmt--; } else if ('z' == pFmt[iFmt]) { // size_t/ssize_t length modifier. Map onto the // existing nLongs tiers by actual width so both LP64 // (size_t == unsigned long) and LLP64 (size_t is // 64-bit while long is 32-bit) marshal the va_arg // type the caller actually pushed. Without this, // "%zu" fell through to mux_assert(0) and aborted. // nLongs = (sizeof(size_t) == sizeof(unsigned long)) ? 1 : 2; iFmt++; ncpFmt--; } else if ( '0' <= pFmt[iFmt] && pFmt[iFmt] <= '9') { if (!bSawPeriod) { if (!bWidth) { if ('0' == pFmt[iFmt]) { if (bZeroPadded) { goto done; } bZeroPadded = true; } else { nWidth = pFmt[iFmt] - '0'; bWidth = true; } } else { nWidth = 10 * nWidth + pFmt[iFmt] - '0'; } } else { if (!bPrecision) { nPrecision = pFmt[iFmt] - '0'; bPrecision = true; } else { nPrecision = 10 * nPrecision + pFmt[iFmt] - '0'; } } iFmt++; ncpFmt--; } else if ('.' == pFmt[iFmt]) { bSawPeriod = true; iFmt++; ncpFmt--; } else if ('-' == pFmt[iFmt]) { if (bLeft) { goto done; } bLeft = true; iFmt++; ncpFmt--; } else if ('c' == pFmt[iFmt]) { unsigned int ch; if (bPositional) { if (0 == nArgPos) { nArgPos = nNextSeq++; } if ( nArgPos < 1 || nArgPos > nMaxPos || MPA_CHAR != posArgs[nArgPos].kind) { goto done; } ch = posArgs[nArgPos].v.c; } else { ch = va_arg(va, unsigned int); } if (nLimit < iBuffer + 1) { goto done; } pBuffer[iBuffer] = static_cast(ch); iBuffer++; iFmt++; ncpFmt--; break; } else if ('%' == pFmt[iFmt]) { // "%%" // if (nLimit < iBuffer + 1) { goto done; } pBuffer[iBuffer] = '%'; iBuffer++; iFmt++; ncpFmt--; break; } else { // An unimplemented conversion. This was mux_assert(0), // which is a hard abort() in the shipping build -- // mux_assert has no NDEBUG guard -- so an ordinary // looking T("%+d") took the whole game down. #1382 // produced two wizard-triggerable aborts exactly that // way (@list cache and astbench), and both were fixed // at the call site rather than here (#1429). // // Echo the remainder of the format verbatim and STOP // interpreting. A bounded formatter already truncates // when it runs out of room, so callers are written // against "imperfect output" and not against "no // server"; emitting the text rather than dropping it // leaves the evidence visible without a debugger. // // Stopping is the part that is not optional. No // va_arg was consumed for this spec, so every LATER // conversion in the same format string would read the // wrong argument, and there is no way to resynchronise // without knowing what the unknown spec would have // taken. Continuing was measured (#1445): // // "%+d|%d" 11, 22 -> "%+d|11" wrong arg // "%+d|%s" 11, "tail" -> SIGSEGV // // The second hands an int to the %s path as a UTF8* // and dereferences it. That trades a clean abort for // a wild pointer, which is not an improvement -- so // recovery stops at the first spec it cannot parse. // // Echoing the whole remainder rather than just this // spec keeps the evidence intact: the offending spec // appears in full, with the text that followed it, // while nothing further is interpreted. // size_t nRest = strlen(reinterpret_cast(pFmt) + iFmtSpec); if (nLimit < iBuffer + nRest) { nRest = nLimit - iBuffer; } memcpy(pBuffer + iBuffer, pFmt + iFmtSpec, nRest); iBuffer += nRest; goto done; } } } } if (0 < dDeferred) { // Unravel the deferred copy. // memcpy(pBuffer + iBuffer, pFmt + iFmtDeferred, dDeferred); iBuffer += dDeferred; dDeferred = 0; } } done: pBuffer[iBuffer] = '\0'; return iBuffer; } void DCL_CDECL mux_sprintf(UTF8 *buff, size_t count, const UTF8 *fmt, ...) { va_list ap; va_start(ap, fmt); (void)mux_vsnprintf(buff, count, fmt, ap); va_end(ap); } // As mux_sprintf, but returns the length written -- 0..count-1, the value // mux_vsnprintf already computes and mux_sprintf throws away. // // Callers that append in a loop need it, and until now each grew its own // varargs wrapper because only the va_list form returned a length. There is // one in mail_mod.cpp already; the next conversion of a raw snprintf() that // uses its return value would have made a third, and identical private // copies do not stay identical (see #1667's ADR on append_ljust_field, which // is byte-for-byte duplicated across two modules today). // // Note this is NOT snprintf's return: snprintf answers how much it WOULD // have written, so `n >= size` is how callers detect truncation. This // answers how much it DID write, so truncation shows as a short result // rather than an over-long one. Anything ported from snprintf that tests // the return against the buffer size has to be re-read, not just renamed. // size_t DCL_CDECL mux_snprintf(UTF8 *buff, size_t count, const UTF8 *fmt, ...) { va_list ap; va_start(ap, fmt); size_t n = mux_vsnprintf(buff, count, fmt, ap); va_end(ap); return n; } void DCL_CDECL mux_fprintf(FILE *fp, const UTF8 *fmt, ...) { if (nullptr != fp) { UTF8 Buffer[MBUF_SIZE]; va_list ap; va_start(ap, fmt); size_t nBuffer = mux_vsnprintf(Buffer, sizeof(Buffer), fmt, ap); va_end(ap); fwrite(Buffer, 1, nBuffer, fp); } } // This function acts like fgets except that any data on the end of the // line past the buffer size is truncated instead of being returned on // the next call. // size_t GetLineTrunc(UTF8 *Buffer, size_t nBuffer, FILE *fp) { size_t lenBuffer = 0; if (fgets(reinterpret_cast(Buffer), static_cast(nBuffer), fp)) { lenBuffer = strlen(reinterpret_cast(Buffer)); } if (lenBuffer <= 0) { memcpy(Buffer, "\n", 2); return 1; } if (Buffer[lenBuffer-1] != '\n') { // The line was too long for the buffer. Continue reading until the // end of the line. // UTF8 TruncBuffer[SBUF_SIZE]; size_t lenTruncBuffer; do { if (!fgets(reinterpret_cast(TruncBuffer), sizeof(TruncBuffer), fp)) { break; } lenTruncBuffer = strlen(reinterpret_cast(TruncBuffer)); } while (TruncBuffer[lenTruncBuffer-1] != '\n'); } return lenBuffer; } // Method: Boyer-Moore-Horspool // // This method is a simplification of the Boyer-Moore String Searching // Algorithm, but a useful one. It does not require as much temporary // storage, and the setup costs are not as high as the full Boyer-Moore. // // If we were searching megabytes of data instead of 8KB at most, then // the full Boyer-Moore would make more sense. // #define BMH_LARGE 32767 void BMH_Prepare(BMH_State *bmhs, size_t nPat, const UTF8 *pPat) { if (nPat <= 0) { return; } size_t k; for (k = 0; k < 256; k++) { bmhs->m_d[k] = nPat; } UTF8 chLastPat = pPat[nPat-1]; bmhs->m_skip2 = nPat; for (k = 0; k < nPat - 1; k++) { bmhs->m_d[static_cast(pPat[k])] = nPat - k - 1; if (pPat[k] == chLastPat) { bmhs->m_skip2 = nPat - k - 1; } } bmhs->m_d[static_cast(chLastPat)] = BMH_LARGE; } bool BMH_Execute(BMH_State *bmhs, size_t *pnMatched, size_t nPat, const UTF8 *pPat, size_t nSrc, const UTF8 *pSrc) { if (nPat <= 0) { return false; } for (size_t i = nPat-1; i < nSrc; i += bmhs->m_skip2) { while ((i += bmhs->m_d[static_cast(pSrc[i])]) < nSrc) { ; // Nothing. } if (i < BMH_LARGE) { break; } i -= BMH_LARGE; int j = static_cast(nPat - 1); const UTF8 *s = pSrc + (i - j); while (--j >= 0 && s[j] == pPat[j]) { ; // Nothing. } if (j < 0) { *pnMatched = s-pSrc; return true; } } return false; } bool BMH_StringSearch(size_t *pnMatched, size_t nPat, const UTF8 *pPat, size_t nSrc, const UTF8 *pSrc) { BMH_State bmhs; BMH_Prepare(&bmhs, nPat, pPat); return BMH_Execute(&bmhs, pnMatched, nPat, pPat, nSrc, pSrc); } // BMH_PrepareI - Pre-lowercase the pattern with mux_strlwr() (Unicode-aware) // and prepare the case-sensitive BMH state on the lowered pattern. // // Note: For XOR transforms (the vast majority), byte length is preserved so // offsets map directly. For the ~24 rare literal transforms that change byte // count, the offset in the lowered source may differ from the original. // Callers (grepi) use the offset to extract from the original text, so the // result is correct when byte length is preserved. // void BMH_PrepareI(BMH_State *bmhs, size_t nPat, const UTF8 *pPat) { size_t nLower; UTF8 *pLower = mux_strlwr(pPat, nLower); // Use a persistent buffer since BMH_Execute needs the pattern later. // static UTF8 LoweredPattern[LBUF_SIZE]; if (nLower >= LBUF_SIZE) { nLower = LBUF_SIZE - 1; } memcpy(LoweredPattern, pLower, nLower); LoweredPattern[nLower] = '\0'; BMH_Prepare(bmhs, nLower, LoweredPattern); } bool BMH_ExecuteI(BMH_State *bmhs, size_t *pnMatched, size_t nPat, const UTF8 *pPat, size_t nSrc, const UTF8 *pSrc) { // Lowercase both pattern and source, then delegate to case-sensitive. // size_t nLowerPat; UTF8 *pLowerPat = mux_strlwr(pPat, nLowerPat); // mux_strlwr uses a static buffer, so copy the lowered pattern first. // LBuf LowPat = LBuf_Src("BMH_ExecuteI"); if (nLowerPat >= LBUF_SIZE) nLowerPat = LBUF_SIZE - 1; memcpy(LowPat, pLowerPat, nLowerPat); LowPat[nLowerPat] = '\0'; size_t nLowerSrc; UTF8 *pLowerSrc = mux_strlwr(pSrc, nLowerSrc); // Re-prepare BMH state with lowered pattern since the state from // BMH_PrepareI may have been invalidated. // BMH_State bmhsLocal; BMH_Prepare(&bmhsLocal, nLowerPat, LowPat); return BMH_Execute(&bmhsLocal, pnMatched, nLowerPat, LowPat, nLowerSrc, pLowerSrc); } bool BMH_StringSearchI(size_t *pnMatched, size_t nPat, const UTF8 *pPat, size_t nSrc, const UTF8 *pSrc) { // Lowercase pattern and source, then use case-sensitive search. // size_t nLowerPat; UTF8 *pLowerPat = mux_strlwr(pPat, nLowerPat); LBuf LowPat = LBuf_Src("BMH_StringSearchI"); if (nLowerPat >= LBUF_SIZE) nLowerPat = LBUF_SIZE - 1; memcpy(LowPat, pLowerPat, nLowerPat); LowPat[nLowerPat] = '\0'; size_t nLowerSrc; UTF8 *pLowerSrc = mux_strlwr(pSrc, nLowerSrc); return BMH_StringSearch(pnMatched, nLowerPat, LowPat, nLowerSrc, pLowerSrc); } // find_pattern_delimiter - Find the first ':' in a $-command or ^-listen // pattern that is not part of a '::' escape. A '::' pair represents a literal // ':' in the pattern (useful for regexp non-capturing groups, etc.). Returns // a pointer to the delimiter ':', or nullptr if none is found. // UTF8 *find_pattern_delimiter(UTF8 *str) { UTF8 *p = str; while (*p) { if (':' == *p) { if (':' == p[1]) { p += 2; continue; } return p; } p++; } return nullptr; } // unescape_pattern_colons - Collapse '::' sequences to ':' in-place. // Called on the pattern portion of a $-command or ^-listen after the // delimiter has been found and the string split. // void unescape_pattern_colons(UTF8 *str) { UTF8 *r = str; UTF8 *w = str; while (*r) { if (':' == r[0] && ':' == r[1]) { *w++ = ':'; r += 2; continue; } *w++ = *r++; } *w = '\0'; } // strip_fancy_quotes - Replace Unicode smart/curly quotes with ASCII equivalents // in-place. The string can only shrink (3-byte UTF-8 sequences become 1 byte), // so no additional buffer is needed. // // U+201C / U+201D (left/right double quotation mark) -> " // U+2018 / U+2019 (left/right single quotation mark) -> ' // void strip_fancy_quotes(UTF8 *str) { UTF8 *r = str; UTF8 *w = str; while (*r) { if ( 0xE2 == r[0] && 0x80 == r[1]) { if (0x9C == r[2] || 0x9D == r[2]) { *w++ = '"'; r += 3; continue; } else if (0x98 == r[2] || 0x99 == r[2]) { *w++ = '\''; r += 3; continue; } } *w++ = *r++; } *w = '\0'; } bool mux_fopen(FILE **pFile, const UTF8 *filename, const UTF8 *mode) { if (pFile) { *pFile = nullptr; if ( nullptr != filename && nullptr != mode) { #if defined(WINDOWS_FILES) && !defined(__INTEL_COMPILER) && (_MSC_VER >= 1400) // 1400 is Visual C++ 2005 // return (fopen_s(pFile, reinterpret_cast(filename), reinterpret_cast(mode)) == 0); #else *pFile = fopen(reinterpret_cast(filename), reinterpret_cast(mode)); if (nullptr != *pFile) { return true; } #endif // WINDOWS_FILES } } return false; } void mux_fclose(FILE *fp) { if (nullptr != fp) { fclose(fp); } } bool mux_open(int *pfh, const UTF8 *filename, int oflag) { if (nullptr != pfh) { *pfh = MUX_OPEN_INVALID_HANDLE_VALUE; if (nullptr != filename) { #if defined(WINDOWS_FILES) && !defined(__INTEL_COMPILER) && (_MSC_VER >= 1400) // 1400 is Visual C++ 2005 // return (_sopen_s(pfh, reinterpret_cast(filename), oflag, _SH_DENYNO, _S_IREAD|_S_IWRITE) == 0); #elif defined(WINDOWS_FILES) *pfh = _open(reinterpret_cast(filename), oflag, _S_IREAD|_S_IWRITE); return (0 <= *pfh); #else *pfh = open(reinterpret_cast(filename), oflag, 0600); return (0 <= *pfh); #endif } } return false; } const UTF8 *mux_strerror(int errnum) { #if defined(WINDOWS_FILES) && !defined(__INTEL_COMPILER) && (_MSC_VER >= 1400) // 1400 is Visual C++ 2005 // static UTF8 buffer[80]; strerror_s(reinterpret_cast(buffer), sizeof(buffer), errnum); return buffer; #else return reinterpret_cast(strerror(errnum)); #endif } size_t LeftJustifyString(UTF8 *field, size_t nWidth, const UTF8 *value) { size_t n = strlen(reinterpret_cast(value)); if (n > nWidth) { n = nWidth; } memcpy(field, value, n); memset(field+n, ' ', nWidth-n); return nWidth; } size_t RightJustifyNumber(UTF8 *field, size_t nWidth, int64_t value, UTF8 chFill) { UTF8 buffer[I64BUF_SIZE]; size_t nReturn = 0; if (nWidth < sizeof(buffer)) { size_t n = mux_i64toa(value, buffer); if (n < sizeof(buffer)) { nReturn = n; if (n < nWidth) { memset(field, chFill, nWidth-n); field += nWidth-n; nReturn = nWidth; } memcpy(field, buffer, n); } } return nReturn; } // ConvertCRLFtoSpace — replace CRLF sequences with spaces. // Returns a pointer to a static buffer. // UTF8 *ConvertCRLFtoSpace(const UTF8 *pString) { static UTF8 buf[LBUF_SIZE]; UTF8 *bp = buf; // Skip any leading CRLF. // while ( '\r' == *pString || '\n' == *pString) { pString++; } bool bFirst = true; while (*pString) { if (!bFirst) { safe_chr(' ', buf, &bp); } else { bFirst = false; } while ( *pString && '\r' != *pString && '\n' != *pString) { safe_chr(*pString, buf, &bp); pString++; } // Skip any CRLF. // while ( '\r' == *pString || '\n' == *pString) { pString++; } } *bp = '\0'; return buf; }