blob: 0fb4ff71ca4ff6413d612fad2ca2c356db18ee19 [file] [log] [blame]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmProcessOutput.h"
#if defined(_WIN32)
# include <cm/memory>
# include <windows.h>
unsigned int cmProcessOutput::defaultCodepage =
KWSYS_ENCODING_DEFAULT_CODEPAGE;
#endif
cmProcessOutput::Encoding cmProcessOutput::FindEncoding(
std::string const& name)
{
Encoding encoding = Auto;
if ((name == "UTF8") || (name == "UTF-8")) {
encoding = UTF8;
} else if (name == "NONE") {
encoding = None;
} else if (name == "ANSI") {
encoding = ANSI;
} else if (name == "OEM") {
encoding = OEM;
}
return encoding;
}
cmProcessOutput::cmProcessOutput(Encoding encoding, unsigned int maxSize)
{
#if defined(_WIN32)
codepage = 0;
bufferSize = maxSize;
if (encoding == None) {
codepage = defaultCodepage;
} else if (encoding == Auto) {
codepage = GetConsoleCP();
} else if (encoding == UTF8) {
codepage = CP_UTF8;
} else if (encoding == OEM) {
codepage = GetOEMCP();
}
if (!codepage || encoding == ANSI) {
codepage = GetACP();
}
#else
static_cast<void>(encoding);
static_cast<void>(maxSize);
#endif
}
cmProcessOutput::~cmProcessOutput() = default;
bool cmProcessOutput::DecodeText(std::string raw, std::string& decoded,
size_t id)
{
#if !defined(_WIN32)
static_cast<void>(id);
decoded.swap(raw);
return true;
#else
bool success = true;
decoded = raw;
if (id > 0) {
if (rawparts.size() < id) {
rawparts.reserve(id);
while (rawparts.size() < id)
rawparts.push_back(std::string());
}
raw = rawparts[id - 1] + raw;
rawparts[id - 1].clear();
decoded = raw;
}
if (raw.size() > 0 && codepage != defaultCodepage) {
success = false;
CPINFOEXW cpinfo;
if (id > 0 && bufferSize > 0 && raw.size() == bufferSize &&
GetCPInfoExW(codepage, 0, &cpinfo) == 1 && cpinfo.MaxCharSize > 1) {
if (cpinfo.MaxCharSize == 2 && cpinfo.LeadByte[0] != 0) {
LPSTR prevChar =
CharPrevExA(codepage, raw.c_str(), raw.c_str() + raw.size(), 0);
bool isLeadByte =
(*(prevChar + 1) == 0) && IsDBCSLeadByteEx(codepage, *prevChar);
if (isLeadByte) {
rawparts[id - 1] += *(raw.end() - 1);
raw.resize(raw.size() - 1);
}
success = DoDecodeText(raw, decoded, NULL);
} else {
bool restoreDecoded = false;
std::string firstDecoded = decoded;
wchar_t lastChar = 0;
for (UINT i = 0; i < cpinfo.MaxCharSize; i++) {
success = DoDecodeText(raw, decoded, &lastChar);
if (success && lastChar != 0) {
if (i == 0) {
firstDecoded = decoded;
}
if (lastChar == cpinfo.UnicodeDefaultChar) {
restoreDecoded = true;
rawparts[id - 1] = *(raw.end() - 1) + rawparts[id - 1];
raw.resize(raw.size() - 1);
} else {
restoreDecoded = false;
break;
}
} else {
break;
}
}
if (restoreDecoded) {
decoded = firstDecoded;
rawparts[id - 1].clear();
}
}
} else {
success = DoDecodeText(raw, decoded, NULL);
}
}
return success;
#endif
}
bool cmProcessOutput::DecodeText(const char* data, size_t length,
std::string& decoded, size_t id)
{
return DecodeText(std::string(data, length), decoded, id);
}
bool cmProcessOutput::DecodeText(std::vector<char> raw,
std::vector<char>& decoded, size_t id)
{
std::string str;
const bool success =
DecodeText(std::string(raw.begin(), raw.end()), str, id);
decoded.assign(str.begin(), str.end());
return success;
}
#if defined(_WIN32)
bool cmProcessOutput::DoDecodeText(std::string raw, std::string& decoded,
wchar_t* lastChar)
{
bool success = false;
const int wlength =
MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()), NULL, 0);
auto wdata = cm::make_unique<wchar_t[]>(wlength);
int r = MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()),
wdata.get(), wlength);
if (r > 0) {
if (lastChar) {
*lastChar = 0;
if ((wlength >= 2 && wdata[wlength - 2] != wdata[wlength - 1]) ||
wlength >= 1) {
*lastChar = wdata[wlength - 1];
}
}
int length = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength,
NULL, 0, NULL, NULL);
auto data = cm::make_unique<char[]>(length);
r = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength,
data.get(), length, NULL, NULL);
if (r > 0) {
decoded = std::string(data.get(), length);
success = true;
}
}
return success;
}
#endif