EncodingCXX.cxx 7.45 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
3
#ifdef __osf__
4 5 6
#define _OSF_SOURCE
#define _POSIX_C_SOURCE 199506L
#define _XOPEN_SOURCE_EXTENDED
7 8 9 10 11 12 13 14 15
#endif

#include "kwsysPrivate.h"
#include KWSYS_HEADER(Encoding.hxx)
#include KWSYS_HEADER(Encoding.h)

// Work-around CMake dependency scanning limitation.  This must
// duplicate the above list of headers.
#if 0
16 17
#include "Encoding.h.in"
#include "Encoding.hxx.in"
18 19 20
#endif

#include <stdlib.h>
21
#include <string.h>
22
#include <vector>
23 24

#ifdef _MSC_VER
25
#pragma warning(disable : 4786)
26 27 28 29
#endif

// Windows API.
#if defined(_WIN32)
30 31
#include <windows.h>

32
#include <ctype.h>
33
#include <shellapi.h>
34 35
#endif

36
namespace KWSYS_NAMESPACE {
37

38 39
Encoding::CommandLineArguments Encoding::CommandLineArguments::Main(
  int argc, char const* const* argv)
40 41
{
#ifdef _WIN32
42 43
  (void)argc;
  (void)argv;
44 45 46 47 48 49

  int ac;
  LPWSTR* w_av = CommandLineToArgvW(GetCommandLineW(), &ac);

  std::vector<std::string> av1(ac);
  std::vector<char const*> av2(ac);
50
  for (int i = 0; i < ac; i++) {
51 52
    av1[i] = ToNarrow(w_av[i]);
    av2[i] = av1[i].c_str();
53
  }
54 55 56 57 58 59 60 61 62 63
  LocalFree(w_av);
  return CommandLineArguments(ac, &av2[0]);
#else
  return CommandLineArguments(argc, argv);
#endif
}

Encoding::CommandLineArguments::CommandLineArguments(int ac,
                                                     char const* const* av)
{
64 65
  this->argv_.resize(ac + 1);
  for (int i = 0; i < ac; i++) {
66
    this->argv_[i] = strdup(av[i]);
67
  }
68 69 70 71 72 73
  this->argv_[ac] = 0;
}

Encoding::CommandLineArguments::CommandLineArguments(int ac,
                                                     wchar_t const* const* av)
{
74 75
  this->argv_.resize(ac + 1);
  for (int i = 0; i < ac; i++) {
76
    this->argv_[i] = kwsysEncoding_DupToNarrow(av[i]);
77
  }
78 79 80 81 82
  this->argv_[ac] = 0;
}

Encoding::CommandLineArguments::~CommandLineArguments()
{
83
  for (size_t i = 0; i < this->argv_.size(); i++) {
84
    free(argv_[i]);
85
  }
86 87
}

88 89
Encoding::CommandLineArguments::CommandLineArguments(
  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]) : 0;
94
  }
95 96
}

97 98
Encoding::CommandLineArguments& Encoding::CommandLineArguments::operator=(
  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]) : 0;
109
    }
110
  }
111 112 113 114 115 116 117 118 119 120 121 122 123 124

  return *this;
}

int Encoding::CommandLineArguments::argc() const
{
  return static_cast<int>(this->argv_.size() - 1);
}

char const* const* Encoding::CommandLineArguments::argv() const
{
  return &this->argv_[0];
}

125 126
#if KWSYS_STL_HAS_WSTRING

Brad King's avatar
Brad King committed
127
std::wstring Encoding::ToWide(const std::string& str)
128
{
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
  std::wstring wstr;
#if defined(_WIN32)
  const int wlength = MultiByteToWideChar(
    KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), int(str.size()), NULL, 0);
  if (wlength > 0) {
    wchar_t* wdata = new wchar_t[wlength];
    int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
                                int(str.size()), wdata, wlength);
    if (r > 0) {
      wstr = std::wstring(wdata, wlength);
    }
    delete[] wdata;
  }
#else
  size_t pos = 0;
  size_t nullPos = 0;
  do {
    if (pos < str.size() && str.at(pos) != '\0') {
      wstr += ToWide(str.c_str() + pos);
    }
    nullPos = str.find('\0', pos);
150
    if (nullPos != std::string::npos) {
151 152 153
      pos = nullPos + 1;
      wstr += wchar_t('\0');
    }
154
  } while (nullPos != std::string::npos);
155 156
#endif
  return wstr;
157 158
}

Brad King's avatar
Brad King committed
159
std::string Encoding::ToNarrow(const std::wstring& str)
160
{
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
  std::string nstr;
#if defined(_WIN32)
  int length =
    WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
                        int(str.size()), NULL, 0, NULL, NULL);
  if (length > 0) {
    char* data = new char[length];
    int r =
      WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(),
                          int(str.size()), data, length, NULL, NULL);
    if (r > 0) {
      nstr = std::string(data, length);
    }
    delete[] data;
  }
#else
  size_t pos = 0;
  size_t nullPos = 0;
  do {
    if (pos < str.size() && str.at(pos) != '\0') {
      nstr += ToNarrow(str.c_str() + pos);
    }
    nullPos = str.find(wchar_t('\0'), pos);
184
    if (nullPos != std::string::npos) {
185 186 187
      pos = nullPos + 1;
      nstr += '\0';
    }
188
  } while (nullPos != std::string::npos);
189 190
#endif
  return nstr;
191 192
}

Brad King's avatar
Brad King committed
193
std::wstring Encoding::ToWide(const char* cstr)
194
{
Brad King's avatar
Brad King committed
195
  std::wstring wstr;
196
  size_t length = kwsysEncoding_mbstowcs(0, cstr, 0) + 1;
197
  if (length > 0) {
Brad King's avatar
Brad King committed
198
    std::vector<wchar_t> wchars(length);
199
    if (kwsysEncoding_mbstowcs(&wchars[0], cstr, length) > 0) {
200 201
      wstr = &wchars[0];
    }
202
  }
203 204 205
  return wstr;
}

Brad King's avatar
Brad King committed
206
std::string Encoding::ToNarrow(const wchar_t* wcstr)
207
{
Brad King's avatar
Brad King committed
208
  std::string str;
209
  size_t length = kwsysEncoding_wcstombs(0, wcstr, 0) + 1;
210
  if (length > 0) {
211
    std::vector<char> chars(length);
212
    if (kwsysEncoding_wcstombs(&chars[0], wcstr, length) > 0) {
213 214
      str = &chars[0];
    }
215
  }
216 217
  return str;
}
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

#if defined(_WIN32)
// Convert local paths to UNC style paths
std::wstring Encoding::ToWindowsExtendedPath(std::string const& source)
{
  std::wstring wsource = Encoding::ToWide(source);

  // Resolve any relative paths
  DWORD wfull_len;

  /* The +3 is a workaround for a bug in some versions of GetFullPathNameW that
   * won't return a large enough buffer size if the input is too small */
  wfull_len = GetFullPathNameW(wsource.c_str(), 0, NULL, NULL) + 3;
  std::vector<wchar_t> wfull(wfull_len);
  GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], NULL);

  /* This should get the correct size without any extra padding from the
   * previous size workaround. */
  wfull_len = static_cast<DWORD>(wcslen(&wfull[0]));

  if (wfull_len >= 2 && isalpha(wfull[0]) &&
      wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */
    return L"\\\\?\\" + std::wstring(&wfull[0]);
  } else if (wfull_len >= 2 && wfull[0] == L'\\' &&
             wfull[1] == L'\\') { /* Starts with \\ */
    if (wfull_len >= 4 && wfull[2] == L'?' &&
        wfull[3] == L'\\') { /* Starts with \\?\ */
      if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' &&
          wfull[6] == L'C' &&
          wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */
        return std::wstring(&wfull[0]);
      } else if (wfull_len >= 6 && isalpha(wfull[4]) &&
                 wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */
        return std::wstring(&wfull[0]);
      } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */
        return L"\\\\?\\UNC\\" + std::wstring(&wfull[4]);
      }
    } else if (wfull_len >= 4 && wfull[2] == L'.' &&
               wfull[3] == L'\\') { /* Starts with \\.\ a device name */
      if (wfull_len >= 6 && isalpha(wfull[4]) &&
          wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */
        return L"\\\\?\\" + std::wstring(&wfull[4]);
      } else if (wfull_len >=
                 5) { /* \\.\Foo\bar\ Device name is left unchanged */
        return std::wstring(&wfull[0]);
      }
    } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */
      return L"\\\\?\\UNC\\" + std::wstring(&wfull[2]);
    }
  }

  // If this case has been reached, then the path is invalid.  Leave it
  // unchanged
  return Encoding::ToWide(source);
}
#endif

275 276 277
#endif // KWSYS_STL_HAS_WSTRING

} // namespace KWSYS_NAMESPACE