1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
3 #ifdef __osf__
4 # define _OSF_SOURCE
5 # define _POSIX_C_SOURCE 199506L
6 # define _XOPEN_SOURCE_EXTENDED
7 #endif
8
9 #include "kwsysPrivate.h"
10 #include KWSYS_HEADER(Encoding.hxx)
11 #include KWSYS_HEADER(Encoding.h)
12
13 // Work-around CMake dependency scanning limitation. This must
14 // duplicate the above list of headers.
15 #if 0
16 # include "Encoding.h.in"
17 # include "Encoding.hxx.in"
18 #endif
19
20 #include <cstdlib>
21 #include <cstring>
22 #include <vector>
23
24 #ifdef _MSC_VER
25 # pragma warning(disable : 4786)
26 #endif
27
28 // Windows API.
29 #if defined(_WIN32)
30 # include <windows.h>
31
32 # include <ctype.h>
33 # include <shellapi.h>
34 #endif
35
36 namespace KWSYS_NAMESPACE {
37
Main(int argc,char const * const * argv)38 Encoding::CommandLineArguments Encoding::CommandLineArguments::Main(
39 int argc, char const* const* argv)
40 {
41 #ifdef _WIN32
42 (void)argc;
43 (void)argv;
44
45 int ac;
46 LPWSTR* w_av = CommandLineToArgvW(GetCommandLineW(), &ac);
47
48 std::vector<std::string> av1(ac);
49 std::vector<char const*> av2(ac);
50 for (int i = 0; i < ac; i++) {
51 av1[i] = ToNarrow(w_av[i]);
52 av2[i] = av1[i].c_str();
53 }
54 LocalFree(w_av);
55 return CommandLineArguments(ac, &av2[0]);
56 #else
57 return CommandLineArguments(argc, argv);
58 #endif
59 }
60
CommandLineArguments(int ac,char const * const * av)61 Encoding::CommandLineArguments::CommandLineArguments(int ac,
62 char const* const* av)
63 {
64 this->argv_.resize(ac + 1);
65 for (int i = 0; i < ac; i++) {
66 this->argv_[i] = strdup(av[i]);
67 }
68 this->argv_[ac] = nullptr;
69 }
70
CommandLineArguments(int ac,wchar_t const * const * av)71 Encoding::CommandLineArguments::CommandLineArguments(int ac,
72 wchar_t const* const* av)
73 {
74 this->argv_.resize(ac + 1);
75 for (int i = 0; i < ac; i++) {
76 this->argv_[i] = kwsysEncoding_DupToNarrow(av[i]);
77 }
78 this->argv_[ac] = nullptr;
79 }
80
~CommandLineArguments()81 Encoding::CommandLineArguments::~CommandLineArguments()
82 {
83 for (size_t i = 0; i < this->argv_.size(); i++) {
84 free(argv_[i]);
85 }
86 }
87
CommandLineArguments(const CommandLineArguments & other)88 Encoding::CommandLineArguments::CommandLineArguments(
89 const CommandLineArguments& other)
90 {
91 this->argv_.resize(other.argv_.size());
92 for (size_t i = 0; i < this->argv_.size(); i++) {
93 this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr;
94 }
95 }
96
operator =(const CommandLineArguments & other)97 Encoding::CommandLineArguments& Encoding::CommandLineArguments::operator=(
98 const CommandLineArguments& other)
99 {
100 if (this != &other) {
101 size_t i;
102 for (i = 0; i < this->argv_.size(); i++) {
103 free(this->argv_[i]);
104 }
105
106 this->argv_.resize(other.argv_.size());
107 for (i = 0; i < this->argv_.size(); i++) {
108 this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr;
109 }
110 }
111
112 return *this;
113 }
114
argc() const115 int Encoding::CommandLineArguments::argc() const
116 {
117 return static_cast<int>(this->argv_.size() - 1);
118 }
119
argv() const120 char const* const* Encoding::CommandLineArguments::argv() const
121 {
122 return &this->argv_[0];
123 }
124
125 #if KWSYS_STL_HAS_WSTRING
126
ToWide(const std::string & str)127 std::wstring Encoding::ToWide(const std::string& str)
128 {
129 std::wstring wstr;
130 # if defined(_WIN32)
131 const int wlength =
132 MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
133 int(str.size()), nullptr, 0);
134 if (wlength > 0) {
135 wchar_t* wdata = new wchar_t[wlength];
136 int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
137 int(str.size()), wdata, wlength);
138 if (r > 0) {
139 wstr = std::wstring(wdata, wlength);
140 }
141 delete[] wdata;
142 }
143 # else
144 size_t pos = 0;
145 size_t nullPos = 0;
146 do {
147 if (pos < str.size() && str.at(pos) != '\0') {
148 wstr += ToWide(str.c_str() + pos);
149 }
150 nullPos = str.find('\0', pos);
151 if (nullPos != std::string::npos) {
152 pos = nullPos + 1;
153 wstr += wchar_t('\0');
154 }
155 } while (nullPos != std::string::npos);
156 # endif
157 return wstr;
158 }
159
ToNarrow(const std::wstring & str)160 std::string Encoding::ToNarrow(const std::wstring& str)
161 {
162 std::string nstr;
163 # if defined(_WIN32)
164 int length =
165 WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
166 int(str.size()), nullptr, 0, nullptr, nullptr);
167 if (length > 0) {
168 char* data = new char[length];
169 int r =
170 WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
171 int(str.size()), data, length, nullptr, nullptr);
172 if (r > 0) {
173 nstr = std::string(data, length);
174 }
175 delete[] data;
176 }
177 # else
178 size_t pos = 0;
179 size_t nullPos = 0;
180 do {
181 if (pos < str.size() && str.at(pos) != '\0') {
182 nstr += ToNarrow(str.c_str() + pos);
183 }
184 nullPos = str.find(wchar_t('\0'), pos);
185 if (nullPos != std::string::npos) {
186 pos = nullPos + 1;
187 nstr += '\0';
188 }
189 } while (nullPos != std::string::npos);
190 # endif
191 return nstr;
192 }
193
ToWide(const char * cstr)194 std::wstring Encoding::ToWide(const char* cstr)
195 {
196 std::wstring wstr;
197 size_t length = kwsysEncoding_mbstowcs(nullptr, cstr, 0) + 1;
198 if (length > 0) {
199 std::vector<wchar_t> wchars(length);
200 if (kwsysEncoding_mbstowcs(&wchars[0], cstr, length) > 0) {
201 wstr = &wchars[0];
202 }
203 }
204 return wstr;
205 }
206
ToNarrow(const wchar_t * wcstr)207 std::string Encoding::ToNarrow(const wchar_t* wcstr)
208 {
209 std::string str;
210 size_t length = kwsysEncoding_wcstombs(nullptr, wcstr, 0) + 1;
211 if (length > 0) {
212 std::vector<char> chars(length);
213 if (kwsysEncoding_wcstombs(&chars[0], wcstr, length) > 0) {
214 str = &chars[0];
215 }
216 }
217 return str;
218 }
219
220 # if defined(_WIN32)
221 // Convert local paths to UNC style paths
ToWindowsExtendedPath(std::string const & source)222 std::wstring Encoding::ToWindowsExtendedPath(std::string const& source)
223 {
224 return ToWindowsExtendedPath(ToWide(source));
225 }
226
227 // Convert local paths to UNC style paths
ToWindowsExtendedPath(const char * source)228 std::wstring Encoding::ToWindowsExtendedPath(const char* source)
229 {
230 return ToWindowsExtendedPath(ToWide(source));
231 }
232
233 // Convert local paths to UNC style paths
ToWindowsExtendedPath(std::wstring const & wsource)234 std::wstring Encoding::ToWindowsExtendedPath(std::wstring const& wsource)
235 {
236 // Resolve any relative paths
237 DWORD wfull_len;
238
239 /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that
240 * won't return a large enough buffer size if the input is too small */
241 wfull_len = GetFullPathNameW(wsource.c_str(), 0, nullptr, nullptr) + 3;
242 std::vector<wchar_t> wfull(wfull_len);
243 GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], nullptr);
244
245 /* This should get the correct size without any extra padding from the
246 * previous size workaround. */
247 wfull_len = static_cast<DWORD>(wcslen(&wfull[0]));
248
249 if (wfull_len >= 2 && isalpha(wfull[0]) &&
250 wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */
251 return L"\\\\?\\" + std::wstring(&wfull[0]);
252 } else if (wfull_len >= 2 && wfull[0] == L'\\' &&
253 wfull[1] == L'\\') { /* Starts with \\ */
254 if (wfull_len >= 4 && wfull[2] == L'?' &&
255 wfull[3] == L'\\') { /* Starts with \\?\ */
256 if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' &&
257 wfull[6] == L'C' &&
258 wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */
259 return std::wstring(&wfull[0]);
260 } else if (wfull_len >= 6 && isalpha(wfull[4]) &&
261 wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */
262 return std::wstring(&wfull[0]);
263 } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */
264 return L"\\\\?\\UNC\\" + std::wstring(&wfull[4]);
265 }
266 } else if (wfull_len >= 4 && wfull[2] == L'.' &&
267 wfull[3] == L'\\') { /* Starts with \\.\ a device name */
268 if (wfull_len >= 6 && isalpha(wfull[4]) &&
269 wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */
270 return L"\\\\?\\" + std::wstring(&wfull[4]);
271 } else if (wfull_len >=
272 5) { /* \\.\Foo\bar\ Device name is left unchanged */
273 return std::wstring(&wfull[0]);
274 }
275 } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */
276 return L"\\\\?\\UNC\\" + std::wstring(&wfull[2]);
277 }
278 }
279
280 // If this case has been reached, then the path is invalid. Leave it
281 // unchanged
282 return wsource;
283 }
284 # endif
285
286 #endif // KWSYS_STL_HAS_WSTRING
287
288 } // namespace KWSYS_NAMESPACE
289