SystemTools.cxx 128 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
#endif

9
10
11
12
#if defined(_WIN32) &&                                                        \
  (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) ||      \
   defined(__MINGW32__))
#  define KWSYS_WINDOWS_DIRS
13
#else
14
15
16
#  if defined(__SUNPRO_CC)
#    include <fcntl.h>
#  endif
17
18
#endif

19
#include "kwsysPrivate.h"
20
#include KWSYS_HEADER(RegularExpression.hxx)
21
#include KWSYS_HEADER(SystemTools.hxx)
22
#include KWSYS_HEADER(Directory.hxx)
23
#include KWSYS_HEADER(FStream.hxx)
24
#include KWSYS_HEADER(Encoding.h)
25
#include KWSYS_HEADER(Encoding.hxx)
26

27
#include <algorithm>
28
#include <fstream>
29
#include <iostream>
Brad King's avatar
Brad King committed
30
#include <set>
31
#include <sstream>
32
#include <utility>
33
#include <vector>
34

35
36
37
// Work-around CMake dependency scanning limitation.  This must
// duplicate the above list of headers.
#if 0
38
39
40
41
42
#  include "Directory.hxx.in"
#  include "Encoding.hxx.in"
#  include "FStream.hxx.in"
#  include "RegularExpression.hxx.in"
#  include "SystemTools.hxx.in"
43
44
#endif

45
#ifdef _MSC_VER
46
#  pragma warning(disable : 4786)
47
48
#endif

49
#if defined(__sgi) && !defined(__GNUC__)
50
#  pragma set woff 1375 /* base class destructor not virtual */
51
#endif
52

53
54
#include <cctype>
#include <cerrno>
55
#ifdef __QNX__
56
#  include <malloc.h> /* for malloc/free on QNX */
57
#endif
58
59
60
61
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
62

63
#if defined(_WIN32) && !defined(_MSC_VER) && defined(__GNUC__)
64
#  include <strings.h> /* for strcasecmp */
65
66
#endif

67
#ifdef _MSC_VER
68
#  define umask _umask // Note this is still umask on Borland
69
70
#endif

71
72
// support for realpath call
#ifndef _WIN32
73
#  include <climits>
74
75
76
77
78
79
80
81
82
83
#  include <pwd.h>
#  include <sys/ioctl.h>
#  include <sys/time.h>
#  include <sys/wait.h>
#  include <unistd.h>
#  include <utime.h>
#  ifndef __VMS
#    include <sys/param.h>
#    include <termios.h>
#  endif
84
#  include <csignal> /* sigprocmask */
85
86
#endif

87
88
89
90
#ifdef __linux
#  include <linux/fs.h>
#endif

91
92
// Windows API.
#if defined(_WIN32)
93
94
95
96
97
98
99
100
#  include <windows.h>
#  include <winioctl.h>
#  ifndef INVALID_FILE_ATTRIBUTES
#    define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#  endif
#  if defined(_MSC_VER) && _MSC_VER >= 1800
#    define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
#  endif
101
#elif defined(__CYGWIN__)
102
103
#  include <windows.h>
#  undef _WIN32
104
105
#endif

106
#if !KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H
107
extern char** environ;
108
109
#endif

110
#ifdef __CYGWIN__
111
#  include <sys/cygwin.h>
112
113
#endif

Alexander Neundorf's avatar
   
Alexander Neundorf committed
114
// getpwnam doesn't exist on Windows and Cray Xt3/Catamount
Alexander Neundorf's avatar
   
Alexander Neundorf committed
115
// same for TIOCGWINSZ
116
117
#if defined(_WIN32) || defined(__LIBCATAMOUNT__) ||                           \
  (defined(HAVE_GETPWNAM) && HAVE_GETPWNAM == 0)
118
119
#  undef HAVE_GETPWNAM
#  undef HAVE_TTY_INFO
Alexander Neundorf's avatar
   
Alexander Neundorf committed
120
#else
121
122
#  define HAVE_GETPWNAM 1
#  define HAVE_TTY_INFO 1
Alexander Neundorf's avatar
   
Alexander Neundorf committed
123
124
#endif

125
#define VTK_URL_PROTOCOL_REGEX "([a-zA-Z0-9]*)://(.*)"
126
#define VTK_URL_REGEX                                                         \
127
  "([a-zA-Z0-9]*)://(([A-Za-z0-9]+)(:([^:@]+))?@)?([^:@/]*)(:([0-9]+))?/"     \
128
  "(.+)?"
129
#define VTK_URL_BYTE_REGEX "%[0-9a-fA-F][0-9a-fA-F]"
130
#ifdef _MSC_VER
131
#  include <sys/utime.h>
132
#else
133
#  include <utime.h>
134
#endif
Alexander Neundorf's avatar
   
Alexander Neundorf committed
135

136
137
138
// This is a hack to prevent warnings about these functions being
// declared but not referenced.
#if defined(__sgi) && !defined(__GNUC__)
139
#  include <sys/termios.h>
140
namespace KWSYS_NAMESPACE {
141
142
143
144
145
146
147
148
149
class SystemToolsHack
{
public:
  enum
  {
    Ref1 = sizeof(cfgetospeed(0)),
    Ref2 = sizeof(cfgetispeed(0)),
    Ref3 = sizeof(tcgetattr(0, 0)),
    Ref4 = sizeof(tcsetattr(0, 0, 0)),
150
151
    Ref5 = sizeof(cfsetospeed(0, 0)),
    Ref6 = sizeof(cfsetispeed(0, 0))
152
153
154
155
  };
};
}
#endif
156

157
158
159
160
161
162
#if defined(_WIN32) &&                                                        \
  (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) ||      \
   defined(__MINGW32__))
#  include <direct.h>
#  include <io.h>
#  define _unlink unlink
163
#endif
164
165
166

/* The maximum length of a file name.  */
#if defined(PATH_MAX)
167
#  define KWSYS_SYSTEMTOOLS_MAXPATH PATH_MAX
168
#elif defined(MAXPATHLEN)
169
#  define KWSYS_SYSTEMTOOLS_MAXPATH MAXPATHLEN
170
#else
171
#  define KWSYS_SYSTEMTOOLS_MAXPATH 16384
172
#endif
173
#if defined(__WATCOMC__)
174
175
176
177
178
#  include <direct.h>
#  define _mkdir mkdir
#  define _rmdir rmdir
#  define _getcwd getcwd
#  define _chdir chdir
179
#endif
180

181
#if defined(__BEOS__) && !defined(__ZETA__)
182
183
#  include <be/kernel/OS.h>
#  include <be/storage/Path.h>
184
185
186
187
188
189
190
191

// BeOS 5 doesn't have usleep(), but it has snooze(), which is identical.
static inline void usleep(unsigned int msec)
{
  ::snooze(msec);
}

// BeOS 5 also doesn't have realpath(), but its C++ API offers something close.
192
static inline char* realpath(const char* path, char* resolved_path)
193
194
195
{
  const size_t maxlen = KWSYS_SYSTEMTOOLS_MAXPATH;
  snprintf(resolved_path, maxlen, "%s", path);
196
  BPath normalized(resolved_path, nullptr, true);
197
  const char* resolved = normalized.Path();
198
  if (resolved != nullptr) // nullptr == No such file.
199
200
  {
    if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) {
201
202
      return resolved_path;
    }
203
  }
204
  return nullptr; // something went wrong.
205
206
207
}
#endif

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#ifdef _WIN32
static time_t windows_filetime_to_posix_time(const FILETIME& ft)
{
  LARGE_INTEGER date;
  date.HighPart = ft.dwHighDateTime;
  date.LowPart = ft.dwLowDateTime;

  // removes the diff between 1970 and 1601
  date.QuadPart -= ((LONGLONG)(369 * 365 + 89) * 24 * 3600 * 10000000);

  // converts back from 100-nanoseconds to seconds
  return date.QuadPart / 10000000;
}
#endif

223
#ifdef KWSYS_WINDOWS_DIRS
224
#  include <wctype.h>
225
226
227
#  ifdef _MSC_VER
typedef KWSYS_NAMESPACE::SystemTools::mode_t mode_t;
#  endif
228

229
inline int Mkdir(const std::string& dir, const mode_t* mode)
230
{
231
232
233
234
235
  int ret =
    _wmkdir(KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
  if (ret == 0 && mode)
    KWSYS_NAMESPACE::SystemTools::SetPermissions(dir, *mode);
  return ret;
236
}
Brad King's avatar
Brad King committed
237
inline int Rmdir(const std::string& dir)
238
{
239
  return _wrmdir(
240
    KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
241
}
242
243
inline const char* Getcwd(char* buf, unsigned int len)
{
244
  std::vector<wchar_t> w_buf(len);
245
  if (_wgetcwd(&w_buf[0], len)) {
246
247
248
249
250
251
252
253
254
255
    size_t nlen = kwsysEncoding_wcstombs(buf, &w_buf[0], len);
    if (nlen == static_cast<size_t>(-1)) {
      return 0;
    }
    if (nlen < len) {
      // make sure the drive letter is capital
      if (nlen > 1 && buf[1] == ':') {
        buf[0] = toupper(buf[0]);
      }
      return buf;
256
257
    }
  }
258
  return 0;
259
}
Brad King's avatar
Brad King committed
260
inline int Chdir(const std::string& dir)
261
{
262
#  if defined(__BORLANDC__)
263
  return chdir(dir.c_str());
264
#  else
265
  return _wchdir(KWSYS_NAMESPACE::Encoding::ToWide(dir).c_str());
266
#  endif
267
}
268
inline void Realpath(const std::string& path, std::string& resolved_path,
Brad King's avatar
Brad King committed
269
                     std::string* errorMessage = 0)
270
{
Brad King's avatar
Brad King committed
271
  std::wstring tmp = KWSYS_NAMESPACE::Encoding::ToWide(path);
272
  wchar_t* ptemp;
273
  wchar_t fullpath[MAX_PATH];
274
275
276
  DWORD bufferLen = GetFullPathNameW(
    tmp.c_str(), sizeof(fullpath) / sizeof(fullpath[0]), fullpath, &ptemp);
  if (bufferLen < sizeof(fullpath) / sizeof(fullpath[0])) {
277
    resolved_path = KWSYS_NAMESPACE::Encoding::ToNarrow(fullpath);
278
    KWSYS_NAMESPACE::SystemTools::ConvertToUnixSlashes(resolved_path);
279
280
  } else if (errorMessage) {
    if (bufferLen) {
281
      *errorMessage = "Destination path buffer size too small.";
282
    } else if (unsigned int errorId = GetLastError()) {
283
      LPSTR message = nullptr;
284
285
286
      DWORD size = FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
          FORMAT_MESSAGE_IGNORE_INSERTS,
287
288
        nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPSTR)&message, 0, nullptr);
289
290
      *errorMessage = std::string(message, size);
      LocalFree(message);
291
    } else {
292
      *errorMessage = "Unknown error.";
293
    }
294
295

    resolved_path = "";
296
  } else {
297
    resolved_path = path;
298
  }
299
}
300
#else
301
#  include <sys/types.h>
302

303
304
#  include <fcntl.h>
#  include <unistd.h>
305
inline int Mkdir(const std::string& dir, const mode_t* mode)
306
{
307
  return mkdir(dir.c_str(), mode ? *mode : 00777);
308
}
Brad King's avatar
Brad King committed
309
inline int Rmdir(const std::string& dir)
310
{
311
  return rmdir(dir.c_str());
312
}
313
314
inline const char* Getcwd(char* buf, unsigned int len)
{
315
  return getcwd(buf, len);
316
}
317

Brad King's avatar
Brad King committed
318
inline int Chdir(const std::string& dir)
319
{
320
  return chdir(dir.c_str());
321
}
322
inline void Realpath(const std::string& path, std::string& resolved_path,
323
                     std::string* errorMessage = nullptr)
324
{
325
  char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH];
326

327
  errno = 0;
328
329
  char* ret = realpath(path.c_str(), resolved_name);
  if (ret) {
330
    resolved_path = ret;
331
332
  } else if (errorMessage) {
    if (errno) {
333
      *errorMessage = strerror(errno);
334
    } else {
335
      *errorMessage = "Unknown error.";
336
    }
337
338

    resolved_path = "";
339
  } else {
340
341
    // if path resolution fails, return what was passed in
    resolved_path = path;
342
  }
343
}
344
345
#endif

346
347
#if !defined(_WIN32) && defined(__COMO__)
// Hack for como strict mode to avoid defining _SVID_SOURCE or _BSD_SOURCE.
348
349
350
351
352
353
354
extern "C" {
extern FILE* popen(__const char* __command, __const char* __modes) __THROW;
extern int pclose(FILE* __stream) __THROW;
extern char* realpath(__const char* __restrict __name,
                      char* __restrict __resolved) __THROW;
extern char* strdup(__const char* __s) __THROW;
extern int putenv(char* __string) __THROW;
355
356
}
#endif
357

358
namespace KWSYS_NAMESPACE {
359

360
double SystemTools::GetTime()
361
362
363
364
{
#if defined(_WIN32) && !defined(__CYGWIN__)
  FILETIME ft;
  GetSystemTimeAsFileTime(&ft);
365
366
  return (429.4967296 * ft.dwHighDateTime + 0.0000001 * ft.dwLowDateTime -
          11644473600.0);
367
368
#else
  struct timeval t;
369
  gettimeofday(&t, nullptr);
370
  return 1.0 * double(t.tv_sec) + 0.000001 * double(t.tv_usec);
371
372
373
#endif
}

374
/* Type of character storing the environment.  */
375
376
377
#if defined(_WIN32)
typedef wchar_t envchar;
#else
378
using envchar = char;
379
#endif
380

381
382
383
/* Order by environment key only (VAR from VAR=VALUE).  */
struct kwsysEnvCompare
{
384
385
  bool operator()(const envchar* l, const envchar* r) const
  {
386
387
388
#if defined(_WIN32)
    const wchar_t* leq = wcschr(l, L'=');
    const wchar_t* req = wcschr(r, L'=');
389
390
391
392
393
394
395
    size_t llen = leq ? (leq - l) : wcslen(l);
    size_t rlen = req ? (req - r) : wcslen(r);
    if (llen == rlen) {
      return wcsncmp(l, r, llen) < 0;
    } else {
      return wcscmp(l, r) < 0;
    }
396
#else
397
398
    const char* leq = strchr(l, '=');
    const char* req = strchr(r, '=');
399
400
    size_t llen = leq ? static_cast<size_t>(leq - l) : strlen(l);
    size_t rlen = req ? static_cast<size_t>(req - r) : strlen(r);
401
402
403
404
    if (llen == rlen) {
      return strncmp(l, r, llen) < 0;
    } else {
      return strcmp(l, r) < 0;
405
    }
406
407
#endif
  }
408
409
};

410
class kwsysEnvSet : public std::set<const envchar*, kwsysEnvCompare>
411
412
413
414
{
public:
  class Free
  {
415
    const envchar* Env;
416

417
  public:
418
419
420
421
    Free(const envchar* env)
      : Env(env)
    {
    }
422
    ~Free() { free(const_cast<envchar*>(this->Env)); }
423
424
425

    Free(const Free&) = delete;
    Free& operator=(const Free&) = delete;
426
427
  };

428
  const envchar* Release(const envchar* env)
429
  {
430
    const envchar* old = nullptr;
431
    auto i = this->find(env);
432
    if (i != this->end()) {
433
434
435
      old = *i;
      this->erase(i);
    }
436
437
    return old;
  }
438
439
};

440
441
442
#ifdef _WIN32
struct SystemToolsPathCaseCmp
{
Brad King's avatar
Brad King committed
443
  bool operator()(std::string const& l, std::string const& r) const
444
  {
445
#  ifdef _MSC_VER
446
    return _stricmp(l.c_str(), r.c_str()) < 0;
447
#  elif defined(__GNUC__)
448
    return strcasecmp(l.c_str(), r.c_str()) < 0;
449
#  else
450
    return SystemTools::Strucmp(l.c_str(), r.c_str()) < 0;
451
#  endif
452
  }
453
454
455
};
#endif

456
457
458
459
460
/**
 * SystemTools static variables singleton class.
 */
class SystemToolsStatic
{
461
public:
462
  using StringMap = std::map<std::string, std::string>;
463
#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
464
465
466
467
  /**
   * Path translation table from dir to refdir
   * Each time 'dir' will be found it will be replace by 'refdir'
   */
468
  StringMap TranslationMap;
469
#endif
470
#ifdef _WIN32
471
472
  static std::string GetCasePathName(std::string const& pathIn);
  static std::string GetActualCaseForPathCached(std::string const& path);
473
  static const char* GetEnvBuffered(const char* key);
474
475
  std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap;
  std::map<std::string, std::string> EnvMap;
476
#endif
477
#ifdef __CYGWIN__
478
  StringMap Cyg2Win32Map;
479
#endif
480

481
482
483
484
485
486
  /**
   * Actual implementation of ReplaceString.
   */
  static void ReplaceString(std::string& source, const char* replace,
                            size_t replaceSize, const std::string& with);

487
488
489
490
491
  /**
   * Actual implementation of FileIsFullPath.
   */
  static bool FileIsFullPath(const char*, size_t);

492
493
494
495
496
497
  /**
   * Find a filename (file or directory) in the system PATH, with
   * optional extra paths.
   */
  static std::string FindName(
    const std::string& name,
498
    const std::vector<std::string>& userPaths = std::vector<std::string>(),
499
    bool no_system_path = false);
500
501
};

502
503
504
// Do NOT initialize.  Default initialization to zero is necessary.
static SystemToolsStatic* SystemToolsStatics;

505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
#ifdef _WIN32
std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
{
  std::string casePath;

  // First check if the file is relative. We don't fix relative paths since the
  // real case depends on the root directory and the given path fragment may
  // have meaning elsewhere in the project.
  if (!SystemTools::FileIsFullPath(pathIn)) {
    // This looks unnecessary, but it allows for the return value optimization
    // since all return paths return the same local variable.
    casePath = pathIn;
    return casePath;
  }

  std::vector<std::string> path_components;
  SystemTools::SplitPath(pathIn, path_components);

  // Start with root component.
  std::vector<std::string>::size_type idx = 0;
  casePath = path_components[idx++];
  // make sure drive letter is always upper case
  if (casePath.size() > 1 && casePath[1] == ':') {
    casePath[0] = toupper(casePath[0]);
  }
  const char* sep = "";

  // If network path, fill casePath with server/share so FindFirstFile
  // will work after that.  Maybe someday call other APIs to get
  // actual case of servers and shares.
  if (path_components.size() > 2 && path_components[0] == "//") {
    casePath += path_components[idx++];
    casePath += "/";
    casePath += path_components[idx++];
    sep = "/";
  }

  // Convert case of all components that exist.
  bool converting = true;
  for (; idx < path_components.size(); idx++) {
    casePath += sep;
    sep = "/";

    if (converting) {
      // If path component contains wildcards, we skip matching
      // because these filenames are not allowed on windows,
      // and we do not want to match a different file.
      if (path_components[idx].find('*') != std::string::npos ||
          path_components[idx].find('?') != std::string::npos) {
        converting = false;
      } else {
        std::string test_str = casePath;
        test_str += path_components[idx];
        WIN32_FIND_DATAW findData;
        HANDLE hFind =
          ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
        if (INVALID_HANDLE_VALUE != hFind) {
          path_components[idx] = Encoding::ToNarrow(findData.cFileName);
          ::FindClose(hFind);
        } else {
          converting = false;
        }
      }
    }

    casePath += path_components[idx];
  }
  return casePath;
}

std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p)
{
  // Check to see if actual case has already been called
  // for this path, and the result is stored in the PathCaseMap
579
  auto& pcm = SystemToolsStatics->PathCaseMap;
580
  {
581
    auto itr = pcm.find(p);
582
583
584
585
586
587
588
589
590
591
592
593
    if (itr != pcm.end()) {
      return itr->second;
    }
  }
  std::string casePath = SystemToolsStatic::GetCasePathName(p);
  if (casePath.size() <= MAX_PATH) {
    pcm[p] = casePath;
  }
  return casePath;
}
#endif

594
// adds the elements of the env variable path to the arg passed in
Brad King's avatar
Brad King committed
595
void SystemTools::GetPath(std::vector<std::string>& path, const char* env)
596
{
597
  size_t const old_size = path.size();
598
#if defined(_WIN32) && !defined(__CYGWIN__)
599
  const char pathSep = ';';
600
#else
601
  const char pathSep = ':';
602
#endif
603
  if (!env) {
604
    env = "PATH";
605
  }
606
  std::string pathEnv;
607
  if (!SystemTools::GetEnv(env, pathEnv)) {
608
    return;
609
  }
610

611
  // A hack to make the below algorithm work.
612
  if (!pathEnv.empty() && pathEnv.back() != pathSep) {
613
    pathEnv += pathSep;
614
615
  }
  std::string::size_type start = 0;
616
  bool done = false;
617
  while (!done) {
Brad King's avatar
Brad King committed
618
    std::string::size_type endpos = pathEnv.find(pathSep, start);
619
620
621
622
    if (endpos != std::string::npos) {
      path.push_back(pathEnv.substr(start, endpos - start));
      start = endpos + 1;
    } else {
623
624
      done = true;
    }
625
  }
626
  for (auto i = path.begin() + old_size; i != path.end(); ++i) {
627
    SystemTools::ConvertToUnixSlashes(*i);
628
  }
629
630
}

631
#if defined(_WIN32)
632
633
const char* SystemToolsStatic::GetEnvBuffered(const char* key)
{
634
  std::string env;
635
  if (SystemTools::GetEnv(key, env)) {
636
    std::string& menv = SystemToolsStatics->EnvMap[key];
637
638
639
    if (menv != env) {
      menv = std::move(env);
    }
640
    return menv.c_str();
641
  }
642
  return nullptr;
Brad King's avatar
Brad King committed
643
}
644
#endif
Brad King's avatar
Brad King committed
645

646
647
const char* SystemTools::GetEnv(const char* key)
{
648
649
650
651
652
#if defined(_WIN32)
  return SystemToolsStatic::GetEnvBuffered(key);
#else
  return getenv(key);
#endif
653
654
}

Brad King's avatar
Brad King committed
655
const char* SystemTools::GetEnv(const std::string& key)
656
{
657
658
659
660
661
#if defined(_WIN32)
  return SystemToolsStatic::GetEnvBuffered(key.c_str());
#else
  return getenv(key.c_str());
#endif
662
663
}

Brad King's avatar
Brad King committed
664
bool SystemTools::GetEnv(const char* key, std::string& result)
Brad King's avatar
Brad King committed
665
{
666
667
668
#if defined(_WIN32)
  const std::wstring wkey = Encoding::ToWide(key);
  const wchar_t* wv = _wgetenv(wkey.c_str());
669
  if (wv) {
670
671
    result = Encoding::ToNarrow(wv);
    return true;
672
  }
673
#else
Brad King's avatar
Brad King committed
674
  const char* v = getenv(key);
675
  if (v) {
Brad King's avatar
Brad King committed
676
677
    result = v;
    return true;
678
  }
679
#endif
680
  return false;
Brad King's avatar
Brad King committed
681
}
682

Brad King's avatar
Brad King committed
683
bool SystemTools::GetEnv(const std::string& key, std::string& result)
684
685
686
687
{
  return SystemTools::GetEnv(key.c_str(), result);
}

688
689
690
691
692
693
694
695
bool SystemTools::HasEnv(const char* key)
{
#if defined(_WIN32)
  const std::wstring wkey = Encoding::ToWide(key);
  const wchar_t* v = _wgetenv(wkey.c_str());
#else
  const char* v = getenv(key);
#endif
696
  return v != nullptr;
697
698
699
700
701
702
703
}

bool SystemTools::HasEnv(const std::string& key)
{
  return SystemTools::HasEnv(key.c_str());
}

704
705
706
#if KWSYS_CXX_HAS_UNSETENV
/* unsetenv("A") removes A from the environment.
   On older platforms it returns void instead of int.  */
Brad King's avatar
Brad King committed
707
static int kwsysUnPutEnv(const std::string& env)
Bill Hoffman's avatar
Bill Hoffman committed
708
{
709
  size_t pos = env.find('=');
710
  if (pos != std::string::npos) {
711
    std::string name = env.substr(0, pos);
712
    unsetenv(name.c_str());
713
  } else {
714
    unsetenv(env.c_str());
715
  }
716
717
  return 0;
}
Bill Hoffman's avatar
Bill Hoffman committed
718

719
720
721
#elif defined(__CYGWIN__) || defined(__GLIBC__)
/* putenv("A") removes A from the environment.  It must not put the
   memory in the environment because it does not have any "=" syntax.  */
Brad King's avatar
Brad King committed
722
static int kwsysUnPutEnv(const std::string& env)
Bill Hoffman's avatar
Bill Hoffman committed
723
{
724
  int err = 0;
725
  size_t pos = env.find('=');
726
  size_t const len = pos == std::string::npos ? env.size() : pos;
727
728
729
  size_t const sz = len + 1;
  char local_buf[256];
  char* buf = sz > sizeof(local_buf) ? (char*)malloc(sz) : local_buf;
730
  if (!buf) {
731
    return -1;
732
  }
733
  strncpy(buf, env.c_str(), len);
734
  buf[len] = 0;
735
  if (putenv(buf) < 0 && errno != EINVAL) {
736
    err = errno;
737
738
  }
  if (buf != local_buf) {
739
    free(buf);
740
741
  }
  if (err) {
742
743
    errno = err;
    return -1;
744
  }
745
746
747
  return 0;
}

748
749
750
751
752
753
754
755
#elif defined(_WIN32)
/* putenv("A=") places "A=" in the environment, which is as close to
   removal as we can get with the putenv API.  We have to leak the
   most recent value placed in the environment for each variable name
   on program exit in case exit routines access it.  */

static kwsysEnvSet kwsysUnPutEnvSet;

756
static int kwsysUnPutEnv(std::string const& env)
757
{
758
759
  std::wstring wEnv = Encoding::ToWide(env);
  size_t const pos = wEnv.find('=');
760
  size_t const len = pos == std::string::npos ? wEnv.size() : pos;
761
  wEnv.resize(len + 1, L'=');
762
  wchar_t* newEnv = _wcsdup(wEnv.c_str());
763
  if (!newEnv) {
764
    return -1;
765
  }
766
767
  kwsysEnvSet::Free oldEnv(kwsysUnPutEnvSet.Release(newEnv));
  kwsysUnPutEnvSet.insert(newEnv);
768
  return _wputenv(newEnv);
769
770
}

771
772
#else
/* Manipulate the "environ" global directly.  */
Brad King's avatar
Brad King committed
773
static int kwsysUnPutEnv(const std::string& env)
774
{
775
  size_t pos = env.find('=');
776
  size_t const len = pos == std::string::npos ? env.size() : pos;
777
778
  int in = 0;
  int out = 0;
779
780
781
  while (environ[in]) {
    if (strlen(environ[in]) > len && environ[in][len] == '=' &&
        strncmp(env.c_str(), environ[in], len) == 0) {
782
      ++in;
783
    } else {
784
785
      environ[out++] = environ[in++];
    }
786
787
  }
  while (out < in) {
788
    environ[out++] = 0;
789
  }
790
791
  return 0;
}
792
#endif
793
794
795
796
797

#if KWSYS_CXX_HAS_SETENV

/* setenv("A", "B", 1) will set A=B in the environment and makes its
   own copies of the strings.  */
Brad King's avatar
Brad King committed
798
bool SystemTools::PutEnv(const std::string& env)
799
{
800
  size_t pos = env.find('=');
801
  if (pos != std::string::npos) {
802
803
    std::string name = env.substr(0, pos);
    return setenv(name.c_str(), env.c_str() + pos + 1, 1) == 0;
804
  } else {
805
    return kwsysUnPutEnv(env) == 0;
806
  }
Bill Hoffman's avatar
Bill Hoffman committed
807
}
808

Brad King's avatar
Brad King committed
809
bool SystemTools::UnPutEnv(const std::string& env)
810
{
811
  return kwsysUnPutEnv(env) == 0;
Bill Hoffman's avatar
Bill Hoffman committed
812
813
}

814
815
816
817
818
819
820
821
822
#else

/* putenv("A=B") will set A=B in the environment.  Most putenv implementations
   put their argument directly in the environment.  They never free the memory
   on program exit.  Keep an active set of pointers to memory we allocate and
   pass to putenv, one per environment key.  At program exit remove any
   environment values that may still reference memory we allocated.  Then free
   the memory.  This will not affect any environment values we never set.  */

823
824
825
#  ifdef __INTEL_COMPILER
#    pragma warning disable 444 /* base has non-virtual destructor */
#  endif
826

827
class kwsysEnv : public kwsysEnvSet
828
829
830
{
public:
  ~kwsysEnv()
831
832
  {
    for (iterator i = this->begin(); i != this->end(); ++i) {
833
#  if defined(_WIN32)
834
      const std::string s = Encoding::ToNarrow(*i);
835
      kwsysUnPutEnv(s);
836
#  else
837
      kwsysUnPutEnv(*i);
838
#  endif
839
      free(const_cast<envchar*>(*i));
840
    }
841
  }
842
  bool Put(const char* env)
843
  {
844
#  if defined(_WIN32)
845
846
    const std::wstring wEnv = Encoding::ToWide(env);
    wchar_t* newEnv = _wcsdup(wEnv.c_str());
847
#  else
848
    char* newEnv = strdup(env);
849
#  endif
850
    Free oldEnv(this->Release(newEnv));
851
    this->insert(newEnv);
852
#  if defined(_WIN32)
853
    return _wputenv(newEnv) == 0;
854
#  else
855
    return putenv(newEnv) == 0;
856
#  endif
857
  }
858
  bool UnPut(const char* env)
859
  {
860
#  if defined(_WIN32)
861
862
    const std::wstring wEnv = Encoding::ToWide(env);
    Free oldEnv(this->Release(wEnv.c_str()));
863
#  else
864
    Free oldEnv(this->Release(env));
865
#  endif
866
    return kwsysUnPutEnv(env) == 0;
867
  }
868
869
870
871
};

static kwsysEnv kwsysEnvInstance;

Brad King's avatar
Brad King committed
872
bool SystemTools::PutEnv(const std::string& env)
873
{
874
  return kwsysEnvInstance.Put(env.c_str());
875
876
}

Brad King's avatar
Brad King committed
877
bool SystemTools::UnPutEnv(const std::string& env)
878
{
879
  return kwsysEnvInstance.UnPut(env.c_str());
880
881
882
883
}

#endif

884
885
const char* SystemTools::GetExecutableExtension()
{
Bill Hoffman's avatar
Bill Hoffman committed
886
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__VMS)
887
888
889
  return ".exe";
#else
  return "";
890
#endif
891
892
}

Brad King's avatar
Brad King committed
893
FILE* SystemTools::Fopen(const std::string& file, const char* mode)
894
895
{
#ifdef _WIN32
896
897
898
899
  // Remove any 'e', which is supported on UNIX, but not Windows.
  std::wstring trimmedMode = Encoding::