SystemTools.cxx 126 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 <fstream>
28
#include <iostream>
Brad King's avatar
Brad King committed
29
#include <set>
30
#include <sstream>
31
#include <utility>
32
#include <vector>
33

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

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

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

52
53
#include <ctype.h>
#include <errno.h>
54
#ifdef __QNX__
55
#  include <malloc.h> /* for malloc/free on QNX */
56
#endif
57
58
59
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
60
61
#include <time.h>

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

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

70
71
// support for realpath call
#ifndef _WIN32
72
73
74
75
76
77
78
79
80
81
82
83
#  include <limits.h>
#  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
#  include <signal.h> /* sigprocmask */
84
85
#endif

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

90
91
// Windows API.
#if defined(_WIN32)
92
93
94
95
96
97
98
99
#  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
100
#elif defined(__CYGWIN__)
101
102
#  include <windows.h>
#  undef _WIN32
103
104
#endif

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

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

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

124
#define VTK_URL_PROTOCOL_REGEX "([a-zA-Z0-9]*)://(.*)"
125
126
127
#define VTK_URL_REGEX                                                         \
  "([a-zA-Z0-9]*)://(([A-Za-z0-9]+)(:([^:@]+))?@)?([^:@/]+)(:([0-9]+))?/"     \
  "(.+)?"
128

129
#ifdef _MSC_VER
130
#  include <sys/utime.h>
131
#else
132
#  include <utime.h>
133
#endif
Alexander Neundorf's avatar
   
Alexander Neundorf committed
134

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

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

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

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

// 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.
191
static inline char* realpath(const char* path, char* resolved_path)
192
193
194
{
  const size_t maxlen = KWSYS_SYSTEMTOOLS_MAXPATH;
  snprintf(resolved_path, maxlen, "%s", path);
195
  BPath normalized(resolved_path, nullptr, true);
196
  const char* resolved = normalized.Path();
197
  if (resolved != nullptr) // nullptr == No such file.
198
199
  {
    if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) {
200
201
      return resolved_path;
    }
202
  }
203
  return nullptr; // something went wrong.
204
205
206
}
#endif

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#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

222
#ifdef KWSYS_WINDOWS_DIRS
223
#  include <wctype.h>
224

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

    resolved_path = "";
289
  } else {
290
    resolved_path = path;
291
  }
292
}
293
#else
294
#  include <sys/types.h>
295

296
297
#  include <fcntl.h>
#  include <unistd.h>
Brad King's avatar
Brad King committed
298
inline int Mkdir(const std::string& dir)
299
{
300
  return mkdir(dir.c_str(), 00777);
301
}
Brad King's avatar
Brad King committed
302
inline int Rmdir(const std::string& dir)
303
{
304
  return rmdir(dir.c_str());
305
}
306
307
inline const char* Getcwd(char* buf, unsigned int len)
{
308
  return getcwd(buf, len);
309
}
310

Brad King's avatar
Brad King committed
311
inline int Chdir(const std::string& dir)
312
{
313
  return chdir(dir.c_str());
314
}
315
inline void Realpath(const std::string& path, std::string& resolved_path,
316
                     std::string* errorMessage = nullptr)
317
{
318
  char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH];
319

320
  errno = 0;
321
322
  char* ret = realpath(path.c_str(), resolved_name);
  if (ret) {
323
    resolved_path = ret;
324
325
  } else if (errorMessage) {
    if (errno) {
326
      *errorMessage = strerror(errno);
327
    } else {
328
      *errorMessage = "Unknown error.";
329
    }
330
331

    resolved_path = "";
332
  } else {
333
334
    // if path resolution fails, return what was passed in
    resolved_path = path;
335
  }
336
}
337
338
#endif

339
340
#if !defined(_WIN32) && defined(__COMO__)
// Hack for como strict mode to avoid defining _SVID_SOURCE or _BSD_SOURCE.
341
342
343
344
345
346
347
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;
348
349
}
#endif
350

351
namespace KWSYS_NAMESPACE {
352

353
354
355
356
357
double SystemTools::GetTime(void)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
  FILETIME ft;
  GetSystemTimeAsFileTime(&ft);
358
359
  return (429.4967296 * ft.dwHighDateTime + 0.0000001 * ft.dwLowDateTime -
          11644473600.0);
360
361
#else
  struct timeval t;
362
  gettimeofday(&t, nullptr);
363
  return 1.0 * double(t.tv_sec) + 0.000001 * double(t.tv_usec);
364
365
366
#endif
}

367
/* Type of character storing the environment.  */
368
369
370
#if defined(_WIN32)
typedef wchar_t envchar;
#else
371
typedef char envchar;
372
#endif
373

374
375
376
/* Order by environment key only (VAR from VAR=VALUE).  */
struct kwsysEnvCompare
{
377
378
  bool operator()(const envchar* l, const envchar* r) const
  {
379
380
381
#if defined(_WIN32)
    const wchar_t* leq = wcschr(l, L'=');
    const wchar_t* req = wcschr(r, L'=');
382
383
384
385
386
387
388
    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;
    }
389
#else
390
391
    const char* leq = strchr(l, '=');
    const char* req = strchr(r, '=');
392
393
    size_t llen = leq ? static_cast<size_t>(leq - l) : strlen(l);
    size_t rlen = req ? static_cast<size_t>(req - r) : strlen(r);
394
395
396
397
    if (llen == rlen) {
      return strncmp(l, r, llen) < 0;
    } else {
      return strcmp(l, r) < 0;
398
    }
399
400
#endif
  }
401
402
};

403
class kwsysEnvSet : public std::set<const envchar*, kwsysEnvCompare>
404
405
406
407
{
public:
  class Free
  {
408
    const envchar* Env;
409

410
  public:
411
412
413
414
    Free(const envchar* env)
      : Env(env)
    {
    }
415
    ~Free() { free(const_cast<envchar*>(this->Env)); }
416
417
418

    Free(const Free&) = delete;
    Free& operator=(const Free&) = delete;
419
420
  };

421
  const envchar* Release(const envchar* env)
422
  {
423
    const envchar* old = nullptr;
424
    iterator i = this->find(env);
425
    if (i != this->end()) {
426
427
428
      old = *i;
      this->erase(i);
    }
429
430
    return old;
  }
431
432
};

433
434
435
#ifdef _WIN32
struct SystemToolsPathCaseCmp
{
Brad King's avatar
Brad King committed
436
  bool operator()(std::string const& l, std::string const& r) const
437
  {
438
#  ifdef _MSC_VER
439
    return _stricmp(l.c_str(), r.c_str()) < 0;
440
#  elif defined(__GNUC__)
441
    return strcasecmp(l.c_str(), r.c_str()) < 0;
442
#  else
443
    return SystemTools::Strucmp(l.c_str(), r.c_str()) < 0;
444
#  endif
445
  }
446
447
448
};
#endif

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

474
475
476
477
478
479
  /**
   * Actual implementation of ReplaceString.
   */
  static void ReplaceString(std::string& source, const char* replace,
                            size_t replaceSize, const std::string& with);

480
481
482
483
484
  /**
   * Actual implementation of FileIsFullPath.
   */
  static bool FileIsFullPath(const char*, size_t);

485
486
487
488
489
490
491
492
  /**
   * Find a filename (file or directory) in the system PATH, with
   * optional extra paths.
   */
  static std::string FindName(
    const std::string& name,
    const std::vector<std::string>& path = std::vector<std::string>(),
    bool no_system_path = false);
493
494
};

495
496
497
498
499
500
501
502
503
504
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
#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
  auto& pcm = SystemTools::Statics->PathCaseMap;
  {
571
    auto itr = pcm.find(p);
572
573
574
575
576
577
578
579
580
581
582
583
    if (itr != pcm.end()) {
      return itr->second;
    }
  }
  std::string casePath = SystemToolsStatic::GetCasePathName(p);
  if (casePath.size() <= MAX_PATH) {
    pcm[p] = casePath;
  }
  return casePath;
}
#endif

584
// adds the elements of the env variable path to the arg passed in
Brad King's avatar
Brad King committed
585
void SystemTools::GetPath(std::vector<std::string>& path, const char* env)
586
{
587
  size_t const old_size = path.size();
588
#if defined(_WIN32) && !defined(__CYGWIN__)
589
  const char pathSep = ';';
590
#else
591
  const char pathSep = ':';
592
#endif
593
  if (!env) {
594
    env = "PATH";
595
  }
596
  std::string pathEnv;
597
  if (!SystemTools::GetEnv(env, pathEnv)) {
598
    return;
599
  }
600

601
  // A hack to make the below algorithm work.
602
  if (!pathEnv.empty() && pathEnv.back() != pathSep) {
603
    pathEnv += pathSep;
604
605
  }
  std::string::size_type start = 0;
606
  bool done = false;
607
  while (!done) {
Brad King's avatar
Brad King committed
608
    std::string::size_type endpos = pathEnv.find(pathSep, start);
609
610
611
612
    if (endpos != std::string::npos) {
      path.push_back(pathEnv.substr(start, endpos - start));
      start = endpos + 1;
    } else {
613
614
      done = true;
    }
615
616
617
  }
  for (std::vector<std::string>::iterator i = path.begin() + old_size;
       i != path.end(); ++i) {
618
    SystemTools::ConvertToUnixSlashes(*i);
619
  }
620
621
}

622
#if defined(_WIN32)
623
624
const char* SystemToolsStatic::GetEnvBuffered(const char* key)
{
625
  std::string env;
626
  if (SystemTools::GetEnv(key, env)) {
627
    std::string& menv = SystemTools::Statics->EnvMap[key];
628
629
630
    if (menv != env) {
      menv = std::move(env);
    }
631
    return menv.c_str();
632
  }
633
  return nullptr;
Brad King's avatar
Brad King committed
634
}
635
#endif
Brad King's avatar
Brad King committed
636

637
638
const char* SystemTools::GetEnv(const char* key)
{
639
640
641
642
643
#if defined(_WIN32)
  return SystemToolsStatic::GetEnvBuffered(key);
#else
  return getenv(key);
#endif
644
645
}

Brad King's avatar
Brad King committed
646
const char* SystemTools::GetEnv(const std::string& key)
647
{
648
649
650
651
652
#if defined(_WIN32)
  return SystemToolsStatic::GetEnvBuffered(key.c_str());
#else
  return getenv(key.c_str());
#endif
653
654
}

Brad King's avatar
Brad King committed
655
bool SystemTools::GetEnv(const char* key, std::string& result)
Brad King's avatar
Brad King committed
656
{
657
658
659
#if defined(_WIN32)
  const std::wstring wkey = Encoding::ToWide(key);
  const wchar_t* wv = _wgetenv(wkey.c_str());
660
  if (wv) {
661
662
    result = Encoding::ToNarrow(wv);
    return true;
663
  }
664
#else
Brad King's avatar
Brad King committed
665
  const char* v = getenv(key);
666
  if (v) {
Brad King's avatar
Brad King committed
667
668
    result = v;
    return true;
669
  }
670
#endif
671
  return false;
Brad King's avatar
Brad King committed
672
}
673

Brad King's avatar
Brad King committed
674
bool SystemTools::GetEnv(const std::string& key, std::string& result)
675
676
677
678
{
  return SystemTools::GetEnv(key.c_str(), result);
}

679
680
681
682
683
684
685
686
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
687
  return v != nullptr;
688
689
690
691
692
693
694
}

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

695
696
697
#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
698
static int kwsysUnPutEnv(const std::string& env)
Bill Hoffman's avatar
Bill Hoffman committed
699
{
700
  size_t pos = env.find('=');
701
  if (pos != std::string::npos) {
702
    std::string name = env.substr(0, pos);
703
    unsetenv(name.c_str());
704
  } else {
705
    unsetenv(env.c_str());
706
  }
707
708
  return 0;
}
Bill Hoffman's avatar
Bill Hoffman committed
709

710
711
712
#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
713
static int kwsysUnPutEnv(const std::string& env)
Bill Hoffman's avatar
Bill Hoffman committed
714
{
715
  int err = 0;
716
  size_t pos = env.find('=');
717
  size_t const len = pos == std::string::npos ? env.size() : pos;
718
719
720
  size_t const sz = len + 1;
  char local_buf[256];
  char* buf = sz > sizeof(local_buf) ? (char*)malloc(sz) : local_buf;
721
  if (!buf) {
722
    return -1;
723
  }
724
  strncpy(buf, env.c_str(), len);
725
  buf[len] = 0;
726
  if (putenv(buf) < 0 && errno != EINVAL) {
727
    err = errno;
728
729
  }
  if (buf != local_buf) {
730
    free(buf);
731
732
  }
  if (err) {
733
734
    errno = err;
    return -1;
735
  }
736
737
738
  return 0;
}

739
740
741
742
743
744
745
746
#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;

747
static int kwsysUnPutEnv(std::string const& env)
748
{
749
750
  std::wstring wEnv = Encoding::ToWide(env);
  size_t const pos = wEnv.find('=');
751
  size_t const len = pos == std::string::npos ? wEnv.size() : pos;
752
  wEnv.resize(len + 1, L'=');
753
  wchar_t* newEnv = _wcsdup(wEnv.c_str());
754
  if (!newEnv) {
755
    return -1;
756
  }
757
758
  kwsysEnvSet::Free oldEnv(kwsysUnPutEnvSet.Release(newEnv));
  kwsysUnPutEnvSet.insert(newEnv);
759
  return _wputenv(newEnv);
760
761
}

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

#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
789
bool SystemTools::PutEnv(const std::string& env)
790
{
791
  size_t pos = env.find('=');
792
  if (pos != std::string::npos) {
793
794
    std::string name = env.substr(0, pos);
    return setenv(name.c_str(), env.c_str() + pos + 1, 1) == 0;
795
  } else {
796
    return kwsysUnPutEnv(env) == 0;
797
  }
Bill Hoffman's avatar
Bill Hoffman committed
798
}
799

Brad King's avatar
Brad King committed
800
bool SystemTools::UnPutEnv(const std::string& env)
801
{
802
  return kwsysUnPutEnv(env) == 0;
Bill Hoffman's avatar
Bill Hoffman committed
803
804
}

805
806
807
808
809
810
811
812
813
#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.  */

814
815
816
#  ifdef __INTEL_COMPILER
#    pragma warning disable 444 /* base has non-virtual destructor */
#  endif
817

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

static kwsysEnv kwsysEnvInstance;

Brad King's avatar
Brad King committed
863
bool SystemTools::PutEnv(const std::string& env)
864
{
865
  return kwsysEnvInstance.Put(env.c_str());
866
867
}

Brad King's avatar
Brad King committed
868
bool SystemTools::UnPutEnv(const std::string& env)
869
{
870
  return kwsysEnvInstance.UnPut(env.c_str());
871
872
873
874
}

#endif

875
876
const char* SystemTools::GetExecutableExtension()
{
Bill Hoffman's avatar
Bill Hoffman committed
877
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__VMS)
878
879
880
  return ".exe";
#else
  return "";
Brad King's avatar