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 <stdlib.h>
21 #include <string.h>
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] = KWSYS_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] = KWSYS_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]) : KWSYS_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]) : KWSYS_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 = MultiByteToWideChar(
132     KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), int(str.size()), NULL, 0);
133   if (wlength > 0) {
134     wchar_t* wdata = new wchar_t[wlength];
135     int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
136                                 int(str.size()), wdata, wlength);
137     if (r > 0) {
138       wstr = std::wstring(wdata, wlength);
139     }
140     delete[] wdata;
141   }
142 #  else
143   size_t pos = 0;
144   size_t nullPos = 0;
145   do {
146     if (pos < str.size() && str.at(pos) != '\0') {
147       wstr += ToWide(str.c_str() + pos);
148     }
149     nullPos = str.find('\0', pos);
150     if (nullPos != std::string::npos) {
151       pos = nullPos + 1;
152       wstr += wchar_t('\0');
153     }
154   } while (nullPos != std::string::npos);
155 #  endif
156   return wstr;
157 }
158 
ToNarrow(const std::wstring & str)159 std::string Encoding::ToNarrow(const std::wstring& str)
160 {
161   std::string nstr;
162 #  if defined(_WIN32)
163   int length =
164     WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
165                         int(str.size()), NULL, 0, NULL, NULL);
166   if (length > 0) {
167     char* data = new char[length];
168     int r =
169       WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
170                           int(str.size()), data, length, NULL, NULL);
171     if (r > 0) {
172       nstr = std::string(data, length);
173     }
174     delete[] data;
175   }
176 #  else
177   size_t pos = 0;
178   size_t nullPos = 0;
179   do {
180     if (pos < str.size() && str.at(pos) != '\0') {
181       nstr += ToNarrow(str.c_str() + pos);
182     }
183     nullPos = str.find(wchar_t('\0'), pos);
184     if (nullPos != std::string::npos) {
185       pos = nullPos + 1;
186       nstr += '\0';
187     }
188   } while (nullPos != std::string::npos);
189 #  endif
190   return nstr;
191 }
192 
ToWide(const char * cstr)193 std::wstring Encoding::ToWide(const char* cstr)
194 {
195   std::wstring wstr;
196   size_t length = kwsysEncoding_mbstowcs(KWSYS_NULLPTR, cstr, 0) + 1;
197   if (length > 0) {
198     std::vector<wchar_t> wchars(length);
199     if (kwsysEncoding_mbstowcs(&wchars[0], cstr, length) > 0) {
200       wstr = &wchars[0];
201     }
202   }
203   return wstr;
204 }
205 
ToNarrow(const wchar_t * wcstr)206 std::string Encoding::ToNarrow(const wchar_t* wcstr)
207 {
208   std::string str;
209   size_t length = kwsysEncoding_wcstombs(KWSYS_NULLPTR, wcstr, 0) + 1;
210   if (length > 0) {
211     std::vector<char> chars(length);
212     if (kwsysEncoding_wcstombs(&chars[0], wcstr, length) > 0) {
213       str = &chars[0];
214     }
215   }
216   return str;
217 }
218 
219 #  if defined(_WIN32)
220 // Convert local paths to UNC style paths
ToWindowsExtendedPath(std::string const & source)221 std::wstring Encoding::ToWindowsExtendedPath(std::string const& source)
222 {
223   std::wstring wsource = Encoding::ToWide(source);
224 
225   // Resolve any relative paths
226   DWORD wfull_len;
227 
228   /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that
229    * won't return a large enough buffer size if the input is too small */
230   wfull_len = GetFullPathNameW(wsource.c_str(), 0, NULL, NULL) + 3;
231   std::vector<wchar_t> wfull(wfull_len);
232   GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], NULL);
233 
234   /* This should get the correct size without any extra padding from the
235    * previous size workaround. */
236   wfull_len = static_cast<DWORD>(wcslen(&wfull[0]));
237 
238   if (wfull_len >= 2 && isalpha(wfull[0]) &&
239       wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */
240     return L"\\\\?\\" + std::wstring(&wfull[0]);
241   } else if (wfull_len >= 2 && wfull[0] == L'\\' &&
242              wfull[1] == L'\\') { /* Starts with \\ */
243     if (wfull_len >= 4 && wfull[2] == L'?' &&
244         wfull[3] == L'\\') { /* Starts with \\?\ */
245       if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' &&
246           wfull[6] == L'C' &&
247           wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */
248         return std::wstring(&wfull[0]);
249       } else if (wfull_len >= 6 && isalpha(wfull[4]) &&
250                  wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */
251         return std::wstring(&wfull[0]);
252       } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */
253         return L"\\\\?\\UNC\\" + std::wstring(&wfull[4]);
254       }
255     } else if (wfull_len >= 4 && wfull[2] == L'.' &&
256                wfull[3] == L'\\') { /* Starts with \\.\ a device name */
257       if (wfull_len >= 6 && isalpha(wfull[4]) &&
258           wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */
259         return L"\\\\?\\" + std::wstring(&wfull[4]);
260       } else if (wfull_len >=
261                  5) { /* \\.\Foo\bar\ Device name is left unchanged */
262         return std::wstring(&wfull[0]);
263       }
264     } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */
265       return L"\\\\?\\UNC\\" + std::wstring(&wfull[2]);
266     }
267   }
268 
269   // If this case has been reached, then the path is invalid.  Leave it
270   // unchanged
271   return Encoding::ToWide(source);
272 }
273 #  endif
274 
275 #endif // KWSYS_STL_HAS_WSTRING
276 
277 } // namespace KWSYS_NAMESPACE
278