1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 
4 #include "cmProcessOutput.h"
5 
6 #if defined(_WIN32)
7 #  include <cm/memory>
8 
9 #  include <windows.h>
10 
11 unsigned int cmProcessOutput::defaultCodepage =
12   KWSYS_ENCODING_DEFAULT_CODEPAGE;
13 #endif
14 
FindEncoding(std::string const & name)15 cmProcessOutput::Encoding cmProcessOutput::FindEncoding(
16   std::string const& name)
17 {
18   Encoding encoding = Auto;
19   if ((name == "UTF8") || (name == "UTF-8")) {
20     encoding = UTF8;
21   } else if (name == "NONE") {
22     encoding = None;
23   } else if (name == "ANSI") {
24     encoding = ANSI;
25   } else if (name == "OEM") {
26     encoding = OEM;
27   }
28   return encoding;
29 }
30 
cmProcessOutput(Encoding encoding,unsigned int maxSize)31 cmProcessOutput::cmProcessOutput(Encoding encoding, unsigned int maxSize)
32 {
33 #if defined(_WIN32)
34   codepage = 0;
35   bufferSize = maxSize;
36   if (encoding == None) {
37     codepage = defaultCodepage;
38   } else if (encoding == Auto) {
39     codepage = GetConsoleCP();
40   } else if (encoding == UTF8) {
41     codepage = CP_UTF8;
42   } else if (encoding == OEM) {
43     codepage = GetOEMCP();
44   }
45   if (!codepage || encoding == ANSI) {
46     codepage = GetACP();
47   }
48 #else
49   static_cast<void>(encoding);
50   static_cast<void>(maxSize);
51 #endif
52 }
53 
DecodeText(std::string raw,std::string & decoded,size_t id)54 bool cmProcessOutput::DecodeText(std::string raw, std::string& decoded,
55                                  size_t id)
56 {
57 #if !defined(_WIN32)
58   static_cast<void>(id);
59   decoded.swap(raw);
60   return true;
61 #else
62   bool success = true;
63   decoded = raw;
64   if (id > 0) {
65     if (rawparts.size() < id) {
66       rawparts.reserve(id);
67       while (rawparts.size() < id)
68         rawparts.push_back(std::string());
69     }
70     raw = rawparts[id - 1] + raw;
71     rawparts[id - 1].clear();
72     decoded = raw;
73   }
74   if (raw.size() > 0 && codepage != defaultCodepage) {
75     success = false;
76     CPINFOEXW cpinfo;
77     if (id > 0 && bufferSize > 0 && raw.size() == bufferSize &&
78         GetCPInfoExW(codepage, 0, &cpinfo) == 1 && cpinfo.MaxCharSize > 1) {
79       if (cpinfo.MaxCharSize == 2 && cpinfo.LeadByte[0] != 0) {
80         LPSTR prevChar =
81           CharPrevExA(codepage, raw.c_str(), raw.c_str() + raw.size(), 0);
82         bool isLeadByte =
83           (*(prevChar + 1) == 0) && IsDBCSLeadByteEx(codepage, *prevChar);
84         if (isLeadByte) {
85           rawparts[id - 1] += *(raw.end() - 1);
86           raw.resize(raw.size() - 1);
87         }
88         success = DoDecodeText(raw, decoded, NULL);
89       } else {
90         bool restoreDecoded = false;
91         std::string firstDecoded = decoded;
92         wchar_t lastChar = 0;
93         for (UINT i = 0; i < cpinfo.MaxCharSize; i++) {
94           success = DoDecodeText(raw, decoded, &lastChar);
95           if (success && lastChar != 0) {
96             if (i == 0) {
97               firstDecoded = decoded;
98             }
99             if (lastChar == cpinfo.UnicodeDefaultChar) {
100               restoreDecoded = true;
101               rawparts[id - 1] = *(raw.end() - 1) + rawparts[id - 1];
102               raw.resize(raw.size() - 1);
103             } else {
104               restoreDecoded = false;
105               break;
106             }
107           } else {
108             break;
109           }
110         }
111         if (restoreDecoded) {
112           decoded = firstDecoded;
113           rawparts[id - 1].clear();
114         }
115       }
116     } else {
117       success = DoDecodeText(raw, decoded, NULL);
118     }
119   }
120   return success;
121 #endif
122 }
123 
DecodeText(const char * data,size_t length,std::string & decoded,size_t id)124 bool cmProcessOutput::DecodeText(const char* data, size_t length,
125                                  std::string& decoded, size_t id)
126 {
127   return this->DecodeText(std::string(data, length), decoded, id);
128 }
129 
DecodeText(std::vector<char> raw,std::vector<char> & decoded,size_t id)130 bool cmProcessOutput::DecodeText(std::vector<char> raw,
131                                  std::vector<char>& decoded, size_t id)
132 {
133   std::string str;
134   const bool success =
135     this->DecodeText(std::string(raw.begin(), raw.end()), str, id);
136   decoded.assign(str.begin(), str.end());
137   return success;
138 }
139 
140 #if defined(_WIN32)
DoDecodeText(std::string raw,std::string & decoded,wchar_t * lastChar)141 bool cmProcessOutput::DoDecodeText(std::string raw, std::string& decoded,
142                                    wchar_t* lastChar)
143 {
144   bool success = false;
145   const int wlength =
146     MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()), NULL, 0);
147   auto wdata = cm::make_unique<wchar_t[]>(wlength);
148   int r = MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()),
149                               wdata.get(), wlength);
150   if (r > 0) {
151     if (lastChar) {
152       *lastChar = 0;
153       if ((wlength >= 2 && wdata[wlength - 2] != wdata[wlength - 1]) ||
154           wlength >= 1) {
155         *lastChar = wdata[wlength - 1];
156       }
157     }
158     int length = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength,
159                                      NULL, 0, NULL, NULL);
160     auto data = cm::make_unique<char[]>(length);
161     r = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength,
162                             data.get(), length, NULL, NULL);
163     if (r > 0) {
164       decoded = std::string(data.get(), length);
165       success = true;
166     }
167   }
168   return success;
169 }
170 #endif
171