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