From 464940d069e6760d446fbdb37fed2f0ec0c79590 Mon Sep 17 00:00:00 2001 From: KWSys Upstream Date: Sat, 30 Nov 2024 07:26:53 -0500 Subject: [PATCH] KWSys 2024-11-30 (dfbcd769) Code extracted from: https://gitlab.kitware.com/utils/kwsys.git at commit dfbcd769aea25157ee65a92f941d0e233fe79826 (master). Upstream Shortlog ----------------- Brad King (9): beaf1ca1 ConsoleBuf: Fix test case when running under Windows Terminal 4feb470a SystemTools: Remove GetActualCaseForPath from CollapseFullPath 6e847d08 SystemInformation: Add missing EOF check when reading /proc/cpuinfo 741c9c96 SystemTools: Expose GetActualCaseForPathCached publicly fdf4f2f8 SystemTools: Fix ReadSymlink for links to absolute paths on Windows 20b2c992 SystemTools: Remove unused global object 47dce1a3 SystemTools: Remove path translation map 30e9db2d SystemTools: Drop GetActualCaseForPathCached 5995fd7d SystemInformation: Ignore stderr from OS query tools Daniel Pfeifer (1): 68e1f35d SystemInformation: fix use of using Jochem van Boxtel (1): 510b13b4 SystemTools: Add FileId class and GetFileId function Juan Ramos (2): ff14b4f5 SystemInformation: Fix find logic a347a66b SystemInformation: Implement HasFPU on Apple processors Mike Lundy (1): ee3223d7 SystemTools: fix clonefile optimization on macOS Peter Kokot (1): 967b2120 Remove legacy unused IMMEDIATE CMake keyword scivision (4): 5f4012c6 Don't compare to nullptr 3f14fce6 Process: Replace 0 with NULL in pointer arguments to GetFullPathNameW 210cea0a SystemTools: Replace unused argument to GetFullPathNameW with nullptr f5e82d63 SystemTools: Replace malloc() with std::string --- CMakeLists.txt | 14 +- Configure.hxx.in | 5 - Directory.cxx | 8 +- ProcessUNIX.c | 2 +- ProcessWin32.c | 4 +- RegularExpression.cxx | 56 +++--- RegularExpression.hxx.in | 6 +- SystemInformation.cxx | 114 +++++-------- SystemTools.cxx | 356 +++++++++++---------------------------- SystemTools.hxx.in | 72 +++++--- testConsoleBuf.cxx | 26 ++- 11 files changed, 252 insertions(+), 411 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 562d5e607d2..bcb3b1795c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -410,14 +410,6 @@ if(KWSYS_USE_DynamicLoader) endif() if(KWSYS_USE_SystemTools) - if (NOT DEFINED KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP) - set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1) - endif () - if (KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP) - set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1) - else () - set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 0) - endif () KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_SETENV "Checking whether CXX compiler has setenv" DIRECT) KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_UNSETENV @@ -692,7 +684,7 @@ foreach(c IN LISTS KWSYS_CLASSES) # Configure the header for this class. configure_file(${PROJECT_SOURCE_DIR}/${c}.hxx.in ${KWSYS_HEADER_DIR}/${c}.hxx - @ONLY IMMEDIATE) + @ONLY) set(KWSYS_CXX_SRCS ${KWSYS_CXX_SRCS} ${KWSYS_HEADER_DIR}/${c}.hxx) # Create an install target for the header. @@ -707,7 +699,7 @@ endforeach() foreach(h IN LISTS KWSYS_H_FILES) # Configure the header into the given directory. configure_file(${PROJECT_SOURCE_DIR}/${h}.h.in ${KWSYS_HEADER_DIR}/${h}.h - @ONLY IMMEDIATE) + @ONLY) set(KWSYS_C_SRCS ${KWSYS_C_SRCS} ${KWSYS_HEADER_DIR}/${h}.h) # Create an install target for the header. @@ -722,7 +714,7 @@ endforeach() foreach(h IN LISTS KWSYS_HXX_FILES) # Configure the header into the given directory. configure_file(${PROJECT_SOURCE_DIR}/${h}.hxx.in ${KWSYS_HEADER_DIR}/${h}.hxx - @ONLY IMMEDIATE) + @ONLY) set(KWSYS_CXX_SRCS ${KWSYS_CXX_SRCS} ${KWSYS_HEADER_DIR}/${h}.hxx) # Create an install target for the header. diff --git a/Configure.hxx.in b/Configure.hxx.in index 8d473405945..b4b0efaa94d 100644 --- a/Configure.hxx.in +++ b/Configure.hxx.in @@ -11,9 +11,6 @@ /* Whether is available. */ #define @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H \ @KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@ -/* Whether the translation map is available or not. */ -#define @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP \ - @KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@ #if defined(__SUNPRO_CC) && __SUNPRO_CC > 0x5130 && defined(__has_attribute) # define @KWSYS_NAMESPACE@_has_cpp_attribute(x) __has_attribute(x) @@ -58,8 +55,6 @@ # define KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H \ @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H # define KWSYS_FALLTHROUGH @KWSYS_NAMESPACE@_FALLTHROUGH -# define KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP \ - @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP #endif #endif diff --git a/Directory.cxx b/Directory.cxx index f152ca4e01d..6e4f0a8c3dc 100644 --- a/Directory.cxx +++ b/Directory.cxx @@ -292,7 +292,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) DIR* dir = opendir(name.c_str()); if (!dir) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return Status::POSIX_errno(); @@ -303,7 +303,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) this->Internal->Files.emplace_back(d->d_name); } if (errno != 0) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return Status::POSIX_errno(); @@ -321,7 +321,7 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, DIR* dir = opendir(name.c_str()); if (!dir) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return 0; @@ -332,7 +332,7 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, count++; } if (errno != 0) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return false; diff --git a/ProcessUNIX.c b/ProcessUNIX.c index b25b2580d28..efe22334f27 100644 --- a/ProcessUNIX.c +++ b/ProcessUNIX.c @@ -2541,7 +2541,7 @@ static void kwsysProcessKill(pid_t process_id) /* Kill all children if we can find them. */ #if defined(__linux__) || defined(__CYGWIN__) /* First try using the /proc filesystem. */ - if ((procdir = opendir("/proc")) != NULL) { + if ((procdir = opendir("/proc"))) { # if defined(MAXPATHLEN) char fname[MAXPATHLEN]; # elif defined(PATH_MAX) diff --git a/ProcessWin32.c b/ProcessWin32.c index 0b43b4a2af7..c1a566f4ad1 100644 --- a/ProcessWin32.c +++ b/ProcessWin32.c @@ -666,14 +666,14 @@ int kwsysProcess_SetWorkingDirectory(kwsysProcess* cp, const char* dir) if (dir && dir[0]) { wchar_t* wdir = kwsysEncoding_DupToWide(dir); /* We must convert the working directory to a full path. */ - DWORD length = GetFullPathNameW(wdir, 0, 0, 0); + DWORD length = GetFullPathNameW(wdir, 0, NULL, NULL); if (length > 0) { wchar_t* work_dir = malloc(length * sizeof(wchar_t)); if (!work_dir) { free(wdir); return 0; } - if (!GetFullPathNameW(wdir, length, work_dir, 0)) { + if (!GetFullPathNameW(wdir, length, work_dir, NULL)) { free(work_dir); free(wdir); return 0; diff --git a/RegularExpression.cxx b/RegularExpression.cxx index b51e16d8ae0..75e7442c28c 100644 --- a/RegularExpression.cxx +++ b/RegularExpression.cxx @@ -48,7 +48,7 @@ RegularExpression::RegularExpression(const RegularExpression& rxp) // Copy pointers into last successful "find" operation this->regmatch = rxp.regmatch; this->regmust = rxp.regmust; // Copy field - if (rxp.regmust != nullptr) { + if (rxp.regmust) { char* dum = rxp.program; ind = 0; while (dum != rxp.regmust) { @@ -81,7 +81,7 @@ RegularExpression& RegularExpression::operator=(const RegularExpression& rxp) // Copy pointers into last successful "find" operation this->regmatch = rxp.regmatch; this->regmust = rxp.regmust; // Copy field - if (rxp.regmust != nullptr) { + if (rxp.regmust) { char* dum = rxp.program; ind = 0; while (dum != rxp.regmust) { @@ -339,7 +339,7 @@ bool RegularExpression::compile(const char* exp) const char* longest; int flags; - if (exp == nullptr) { + if (!exp) { // RAISE Error, SYM(RegularExpression), SYM(No_Expr), printf("RegularExpression::compile(): No expression supplied.\n"); return false; @@ -372,7 +372,7 @@ bool RegularExpression::compile(const char* exp) this->program = new char[comp.regsize]; this->progsize = static_cast(comp.regsize); - if (this->program == nullptr) { + if (!this->program) { // RAISE Error, SYM(RegularExpression), SYM(Out_Of_Memory), printf("RegularExpression::compile(): Out of memory.\n"); return false; @@ -415,7 +415,7 @@ bool RegularExpression::compile(const char* exp) if (flags & SPSTART) { longest = nullptr; size_t len = 0; - for (; scan != nullptr; scan = regnext(scan)) + for (; scan; scan = regnext(scan)) if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { longest = OPERAND(scan); len = strlen(OPERAND(scan)); @@ -461,9 +461,9 @@ char* RegExpCompile::reg(int paren, int* flagp) // Pick up the branches, linking them together. br = regbranch(&flags); - if (br == nullptr) + if (!br) return (nullptr); - if (ret != nullptr) + if (ret) regtail(ret, br); // OPEN -> first. else ret = br; @@ -473,7 +473,7 @@ char* RegExpCompile::reg(int paren, int* flagp) while (*regparse == '|') { regparse++; br = regbranch(&flags); - if (br == nullptr) + if (!br) return (nullptr); regtail(ret, br); // BRANCH -> BRANCH. if (!(flags & HASWIDTH)) @@ -486,7 +486,7 @@ char* RegExpCompile::reg(int paren, int* flagp) regtail(ret, ender); // Hook the tails of the branches to the closing node. - for (br = ret; br != nullptr; br = regnext(br)) + for (br = ret; br; br = regnext(br)) regoptail(br, ender); // Check for proper termination. @@ -527,16 +527,16 @@ char* RegExpCompile::regbranch(int* flagp) chain = nullptr; while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { latest = regpiece(&flags); - if (latest == nullptr) + if (!latest) return (nullptr); *flagp |= flags & HASWIDTH; - if (chain == nullptr) // First piece. + if (!chain) // First piece. *flagp |= flags & SPSTART; else regtail(chain, latest); chain = latest; } - if (chain == nullptr) // Loop ran zero times. + if (!chain) // Loop ran zero times. regnode(NOTHING); return (ret); @@ -559,7 +559,7 @@ char* RegExpCompile::regpiece(int* flagp) int flags; ret = regatom(&flags); - if (ret == nullptr) + if (!ret) return (nullptr); op = *regparse; @@ -678,7 +678,7 @@ char* RegExpCompile::regatom(int* flagp) } break; case '(': ret = reg(1, &flags); - if (ret == nullptr) + if (!ret) return (nullptr); *flagp |= flags & (HASWIDTH | SPSTART); break; @@ -812,7 +812,7 @@ void RegExpCompile::regtail(char* p, const char* val) scan = p; for (;;) { temp = regnext(scan); - if (temp == nullptr) + if (!temp) break; scan = temp; } @@ -831,7 +831,7 @@ void RegExpCompile::regtail(char* p, const char* val) void RegExpCompile::regoptail(char* p, const char* val) { // "Operandless" and "op != BRANCH" are synonymous in practice. - if (p == nullptr || p == regdummyptr || OP(p) != BRANCH) + if (!p || p == regdummyptr || OP(p) != BRANCH) return; regtail(OPERAND(p), val); } @@ -881,14 +881,14 @@ bool RegularExpression::find(char const* string, } // If there is a "must appear" string, look for it. - if (this->regmust != nullptr) { + if (this->regmust) { s = string; - while ((s = strchr(s, this->regmust[0])) != nullptr) { + while ((s = strchr(s, this->regmust[0]))) { if (strncmp(s, this->regmust, this->regmlen) == 0) break; // Found it. s++; } - if (s == nullptr) // Not present. + if (!s) // Not present. return false; } @@ -906,7 +906,7 @@ bool RegularExpression::find(char const* string, s = string; if (this->regstart != '\0') // We know what char it must start with. - while ((s = strchr(s, this->regstart)) != nullptr) { + while ((s = strchr(s, this->regstart))) { if (regFind.regtry(s, rmatch.startp, rmatch.endp, this->program)) return true; s++; @@ -969,7 +969,7 @@ int RegExpFind::regmatch(const char* prog) scan = prog; - while (scan != nullptr) { + while (scan) { next = regnext(scan); @@ -1001,12 +1001,12 @@ int RegExpFind::regmatch(const char* prog) reginput += len; } break; case ANYOF: - if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == nullptr) + if (*reginput == '\0' || !strchr(OPERAND(scan), *reginput)) return (0); reginput++; break; case ANYBUT: - if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != nullptr) + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput)) return (0); reginput++; break; @@ -1058,7 +1058,7 @@ int RegExpFind::regmatch(const char* prog) // Don't set startp if some later invocation of the // same parentheses already has. // - if (regstartp[no] == nullptr) + if (!regstartp[no]) regstartp[no] = save; return (1); } else @@ -1109,7 +1109,7 @@ int RegExpFind::regmatch(const char* prog) // Don't set endp if some later invocation of the // same parentheses already has. // - if (regendp[no] == nullptr) + if (!regendp[no]) regendp[no] = save; return (1); } else @@ -1129,7 +1129,7 @@ int RegExpFind::regmatch(const char* prog) return (1); reginput = save; scan = regnext(scan); - } while (scan != nullptr && OP(scan) == BRANCH); + } while (scan && OP(scan) == BRANCH); return (0); // NOTREACHED } @@ -1207,13 +1207,13 @@ int RegExpFind::regrepeat(const char* p) } break; case ANYOF: - while (*scan != '\0' && strchr(opnd, *scan) != nullptr) { + while (*scan != '\0' && strchr(opnd, *scan)) { count++; scan++; } break; case ANYBUT: - while (*scan != '\0' && strchr(opnd, *scan) == nullptr) { + while (*scan != '\0' && !strchr(opnd, *scan)) { count++; scan++; } diff --git a/RegularExpression.hxx.in b/RegularExpression.hxx.in index 1dc1dfa2089..d73482b0428 100644 --- a/RegularExpression.hxx.in +++ b/RegularExpression.hxx.in @@ -86,7 +86,7 @@ inline RegularExpressionMatch::RegularExpressionMatch() */ inline bool RegularExpressionMatch::isValid() const { - return (this->startp[0] != nullptr); + return (this->startp[0]); } /** @@ -140,7 +140,7 @@ inline std::string::size_type RegularExpressionMatch::end(int n) const */ inline std::string RegularExpressionMatch::match(int n) const { - if (this->startp[n] == nullptr) { + if (!this->startp[n]) { return std::string(); } else { return std::string( @@ -551,7 +551,7 @@ inline bool RegularExpression::operator!=(const RegularExpression& r) const */ inline bool RegularExpression::is_valid() const { - return (this->program != nullptr); + return (this->program); } inline void RegularExpression::set_invalid() diff --git a/SystemInformation.cxx b/SystemInformation.cxx index 5e07e80aaf8..7b0d271196b 100644 --- a/SystemInformation.cxx +++ b/SystemInformation.cxx @@ -59,7 +59,7 @@ # include # endif # if !defined(siginfo_t) -typedef int siginfo_t; +using siginfo_t = int; # endif #else # include @@ -135,7 +135,7 @@ typedef int siginfo_t; using ResourceLimitType = struct rlimit64; # define GetResourceLimit getrlimit64 # else -typedef struct rlimit ResourceLimitType; +using ResourceLimitType = struct rlimit; # define GetResourceLimit getrlimit # endif #elif defined(__hpux) @@ -333,101 +333,58 @@ class SystemInformationImplementation void RunMemoryCheck(); public: - using ID = struct tagID - + struct ID { - int Type; - int Family; - int Model; - int Revision; - int ExtendedFamily; - int ExtendedModel; - std::string ProcessorName; - std::string Vendor; - std::string SerialNumber; - std::string ModelName; }; - using CPUPowerManagement = struct tagCPUPowerManagement - + struct CPUPowerManagement { - bool HasVoltageID; - bool HasFrequencyID; - bool HasTempSenseDiode; }; - using CPUExtendedFeatures = struct tagCPUExtendedFeatures - + struct CPUExtendedFeatures { - bool Has3DNow; - bool Has3DNowPlus; - bool SupportsMP; - bool HasMMXPlus; - bool HasSSEMMX; - unsigned int LogicalProcessorsPerPhysical; - int APIC_ID; - CPUPowerManagement PowerManagement; }; - using CPUFeatures = struct CPUtagFeatures - + struct CPUFeatures { - bool HasFPU; - bool HasTSC; - bool HasMMX; - bool HasSSE; - bool HasSSEFP; - bool HasSSE2; - bool HasIA64; - bool HasAPIC; - bool HasCMOV; - bool HasMTRR; - bool HasACPI; - bool HasSerial; - bool HasThermal; - int CPUSpeed; - int L1CacheSize; - int L2CacheSize; - int L3CacheSize; - CPUExtendedFeatures ExtendedFeatures; }; @@ -874,7 +831,7 @@ int LoadLines(FILE* file, std::vector& lines) char buf[bufSize] = { '\0' }; while (!feof(file) && !ferror(file)) { errno = 0; - if (fgets(buf, bufSize, file) == nullptr) { + if (!fgets(buf, bufSize, file)) { if (ferror(file) && (errno == EINTR)) { clearerr(file); } @@ -900,7 +857,7 @@ int LoadLines(FILE* file, std::vector& lines) int LoadLines(const char* fileName, std::vector& lines) { FILE* file = fopen(fileName, "r"); - if (file == nullptr) { + if (!file) { return 0; } int nRead = LoadLines(file, lines); @@ -938,7 +895,7 @@ int GetFieldsFromFile(const char* fileName, const char** fieldNames, T* values) return -1; } int i = 0; - while (fieldNames[i] != nullptr) { + while (fieldNames[i]) { int ierr = NameValue(fields, fieldNames[i], values[i]); if (ierr) { return -(i + 2); @@ -970,7 +927,7 @@ int GetFieldsFromCommand(const char* command, const char** fieldNames, T* values) { FILE* file = popen(command, "r"); - if (file == nullptr) { + if (!file) { return -1; } std::vector fields; @@ -980,7 +937,7 @@ int GetFieldsFromCommand(const char* command, const char** fieldNames, return -1; } int i = 0; - while (fieldNames[i] != nullptr) { + while (fieldNames[i]) { int ierr = NameValue(fields, fieldNames[i], values[i]); if (ierr) { return -(i + 2); @@ -1016,7 +973,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGFPE: - oss << "Caught SIGFPE at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGFPE at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { # if defined(FPE_INTDIV) @@ -1064,7 +1021,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGSEGV: - oss << "Caught SIGSEGV at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGSEGV at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case SEGV_MAPERR: @@ -1082,7 +1039,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGBUS: - oss << "Caught SIGBUS at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGBUS at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case BUS_ADRALN: @@ -1122,7 +1079,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGILL: - oss << "Caught SIGILL at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGILL at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case ILL_ILLOPC: @@ -1659,7 +1616,7 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( return -2; } - for (ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next) { + for (ifa = ifas; ifa; ifa = ifa->ifa_next) { int fam = ifa->ifa_addr ? ifa->ifa_addr->sa_family : -1; // Skip Loopback interfaces if (((fam == AF_INET) || (fam == AF_INET6)) && @@ -3448,8 +3405,9 @@ bool SystemInformationImplementation::RetrieveInformationFromCpuInfoFile() } size_t fileSize = 0; - while (!feof(fd)) { - buffer += static_cast(fgetc(fd)); + int fc; + while ((fc = fgetc(fd)) != EOF) { + buffer += static_cast(fc); fileSize++; } fclose(fd); @@ -3886,7 +3844,7 @@ long long SystemInformationImplementation::GetProcMemoryUsed() std::ostringstream oss; oss << "ps -o rss= -p " << pid; FILE* file = popen(oss.str().c_str(), "r"); - if (file == nullptr) { + if (!file) { return -1; } oss.str(""); @@ -3923,8 +3881,7 @@ double SystemInformationImplementation::GetLoadAverage() return -0.0; #elif defined(KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes) // Old windows.h headers do not provide GetSystemTimes. - typedef BOOL(WINAPI * GetSystemTimesType)(LPFILETIME, LPFILETIME, - LPFILETIME); + using GetSystemTimesType = BOOL(WINAPI*)(LPFILETIME, LPFILETIME, LPFILETIME); static GetSystemTimesType pGetSystemTimes = (GetSystemTimesType)GetProcAddress(GetModuleHandleW(L"kernel32"), "GetSystemTimes"); @@ -4469,8 +4426,8 @@ void SystemInformationImplementation::CPUCountWindows() this->NumberOfPhysicalCPU = 0; this->NumberOfLogicalCPU = 0; - typedef BOOL(WINAPI * GetLogicalProcessorInformationType)( - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); + using GetLogicalProcessorInformationType = + BOOL(WINAPI*)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); static GetLogicalProcessorInformationType pGetLogicalProcessorInformation = reinterpret_cast(GetProcAddress( GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation")); @@ -4499,10 +4456,7 @@ void SystemInformationImplementation::CPUCountWindows() (void)rc; // Silence unused variable warning } - typedef std::vector::iterator - pinfoIt_t; - for (pinfoIt_t it = ProcInfo.begin(); it != ProcInfo.end(); ++it) { - SYSTEM_LOGICAL_PROCESSOR_INFORMATION PInfo = *it; + for (SYSTEM_LOGICAL_PROCESSOR_INFORMATION const& PInfo : ProcInfo) { if (PInfo.Relationship != RelationProcessorCore) { continue; } @@ -4646,7 +4600,7 @@ bool SystemInformationImplementation::ParseSysCtl() err = sysctlbyname("hw.machine", &tempBuff, &len, nullptr, 0); if (err == 0) { std::string machineBuf(tempBuff); - if (machineBuf.find_first_of("Power") != std::string::npos) { + if (machineBuf.find("Power") != std::string::npos) { this->ChipID.Vendor = "IBM"; err = kw_sysctlbyname_int32("hw.cputype", &tempInt32); @@ -4660,10 +4614,15 @@ bool SystemInformationImplementation::ParseSysCtl() } this->FindManufacturer(); - } else if (machineBuf.find_first_of("arm64") != std::string::npos) { + } else if (machineBuf.find("arm64") != std::string::npos) { this->ChipID.Vendor = "Apple"; this->FindManufacturer(); + + err = kw_sysctlbyname_int32("hw.optional.floatingpoint", &tempInt32); + if (err == 0) { + this->Features.HasFPU = static_cast(tempInt32); + } } } } else { @@ -4800,7 +4759,7 @@ std::string SystemInformationImplementation::ExtractValueFromSysCtl( std::string SystemInformationImplementation::RunProcess( std::vector args) { - std::string buffer; + std::string out; // Run the application kwsysProcess* gp = kwsysProcess_New(); @@ -4819,7 +4778,10 @@ std::string SystemInformationImplementation::RunProcess( (pipe == kwsysProcess_Pipe_STDOUT || pipe == kwsysProcess_Pipe_STDERR))) // wait for 1s { - buffer.append(data, length); + // Keep stdout, ignore stderr. + if (pipe == kwsysProcess_Pipe_STDOUT) { + out.append(data, length); + } } kwsysProcess_WaitForExit(gp, nullptr); @@ -4849,7 +4811,7 @@ std::string SystemInformationImplementation::RunProcess( if (result) { std::cerr << "Error " << args[0] << " returned :" << result << "\n"; } - return buffer; + return out; } std::string SystemInformationImplementation::ParseValueFromKStat( @@ -5459,7 +5421,7 @@ bool SystemInformationImplementation::QueryOSInformation() this->OSVersion = operatingSystem; } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { // Windows XP and .NET server. - typedef BOOL(CALLBACK * LPFNPROC)(HANDLE, BOOL*); + using LPFNPROC = BOOL(CALLBACK*)(HANDLE, BOOL*); HINSTANCE hKernelDLL; LPFNPROC DLLProc; diff --git a/SystemTools.cxx b/SystemTools.cxx index 11018a6befe..5b579004c65 100644 --- a/SystemTools.cxx +++ b/SystemTools.cxx @@ -59,9 +59,6 @@ #include #include -#ifdef __QNX__ -# include /* for malloc/free on QNX */ -#endif #include #include #include @@ -268,7 +265,7 @@ static inline char* realpath(const char* path, char* resolved_path) snprintf(resolved_path, maxlen, "%s", path); BPath normalized(resolved_path, nullptr, true); const char* resolved = normalized.Path(); - if (resolved != nullptr) // nullptr == No such file. + if (resolved) // nullptr == No such file. { if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) { return resolved_path; @@ -338,10 +335,9 @@ inline void Realpath(const std::string& path, std::string& resolved_path, std::string* errorMessage = nullptr) { std::wstring tmp = KWSYS_NAMESPACE::Encoding::ToWide(path); - wchar_t* ptemp; wchar_t fullpath[MAX_PATH]; DWORD bufferLen = GetFullPathNameW( - tmp.c_str(), sizeof(fullpath) / sizeof(fullpath[0]), fullpath, &ptemp); + tmp.c_str(), sizeof(fullpath) / sizeof(fullpath[0]), fullpath, nullptr); if (bufferLen < sizeof(fullpath) / sizeof(fullpath[0])) { resolved_path = KWSYS_NAMESPACE::Encoding::ToNarrow(fullpath); KWSYS_NAMESPACE::SystemTools::ConvertToUnixSlashes(resolved_path); @@ -494,75 +490,17 @@ class kwsysEnvSet : public std::set } }; -#ifdef _WIN32 -# if defined(_WIN64) -static constexpr size_t FNV_OFFSET_BASIS = 14695981039346656037ULL; -static constexpr size_t FNV_PRIME = 1099511628211ULL; -# else -static constexpr size_t FNV_OFFSET_BASIS = 2166136261U; -static constexpr size_t FNV_PRIME = 16777619U; -# endif - -// Case insensitive Fnv1a hash -struct SystemToolsPathCaseHash -{ - size_t operator()(std::string const& path) const - { - size_t hash = FNV_OFFSET_BASIS; - for (auto c : path) { - hash ^= static_cast(std::tolower(c)); - hash *= FNV_PRIME; - } - - return hash; - } -}; - -struct SystemToolsPathCaseEqual -{ - bool operator()(std::string const& l, std::string const& r) const - { -# ifdef _MSC_VER - return _stricmp(l.c_str(), r.c_str()) == 0; -# elif defined(__GNUC__) - return strcasecmp(l.c_str(), r.c_str()) == 0; -# else - return SystemTools::Strucmp(l.c_str(), r.c_str()) == 0; -# endif - } -}; -#endif - /** * SystemTools static variables singleton class. */ class SystemToolsStatic { public: - using StringMap = std::map; -#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP - /** - * Path translation table from dir to refdir - * Each time 'dir' will be found it will be replace by 'refdir' - */ - StringMap TranslationMap; -#endif #ifdef _WIN32 - static std::string GetCasePathName(std::string const& pathIn, - bool const cache); - static std::string GetActualCaseForPathCached(std::string const& path); + static std::string GetCasePathName(std::string const& pathIn); static const char* GetEnvBuffered(const char* key); - std::unordered_map - FindFileMap; - std::unordered_map - PathCaseMap; std::map EnvMap; #endif -#ifdef __CYGWIN__ - StringMap Cyg2Win32Map; -#endif /** * Actual implementation of ReplaceString. @@ -589,8 +527,7 @@ class SystemToolsStatic static SystemToolsStatic* SystemToolsStatics; #ifdef _WIN32 -std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn, - bool const cache) +std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) { std::string casePath; @@ -643,30 +580,15 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn, std::string test_str = casePath; test_str += path_components[idx]; - bool found_in_cache = false; - if (cache) { - auto const it = SystemToolsStatics->FindFileMap.find(test_str); - if (it != SystemToolsStatics->FindFileMap.end()) { - path_components[idx] = it->second; - found_in_cache = true; - } - } - - if (!found_in_cache) { - WIN32_FIND_DATAW findData; - HANDLE hFind = - ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); - if (INVALID_HANDLE_VALUE != hFind) { - auto case_file_name = Encoding::ToNarrow(findData.cFileName); - if (cache) { - SystemToolsStatics->FindFileMap.emplace(test_str, - case_file_name); - } - path_components[idx] = std::move(case_file_name); - ::FindClose(hFind); - } else { - converting = false; - } + WIN32_FIND_DATAW findData; + HANDLE hFind = + ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + auto case_file_name = Encoding::ToNarrow(findData.cFileName); + path_components[idx] = std::move(case_file_name); + ::FindClose(hFind); + } else { + converting = false; } } } @@ -675,21 +597,6 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn, } return casePath; } - -std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p) -{ - std::string casePath; - - auto it = SystemToolsStatics->PathCaseMap.find(p); - if (it != SystemToolsStatics->PathCaseMap.end()) { - casePath = it->second; - } else { - casePath = SystemToolsStatic::GetCasePathName(p, true); - SystemToolsStatics->PathCaseMap.emplace(p, casePath); - } - - return casePath; -} #endif // adds the elements of the env variable path to the arg passed in @@ -798,7 +705,7 @@ bool SystemTools::HasEnv(const char* key) #else const char* v = getenv(key); #endif - return v != nullptr; + return v; } bool SystemTools::HasEnv(const std::string& key) @@ -824,25 +731,14 @@ static int kwsysUnPutEnv(const std::string& env) #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. */ + static int kwsysUnPutEnv(const std::string& env) { int err = 0; - size_t pos = env.find('='); - size_t const len = pos == std::string::npos ? env.size() : pos; - size_t const sz = len + 1; - char local_buf[256]; - char* buf = sz > sizeof(local_buf) ? (char*)malloc(sz) : local_buf; - if (!buf) { - return -1; - } - strncpy(buf, env.c_str(), len); - buf[len] = 0; - if (putenv(buf) < 0 && errno != EINVAL) { + std::string buf = env.substr(0, env.find('=')); + if (putenv(&buf[0]) < 0 && errno != EINVAL) { err = errno; } - if (buf != local_buf) { - free(buf); - } if (err) { errno = err; return -1; @@ -1167,7 +1063,7 @@ static DWORD SystemToolsMakeRegistryMode(DWORD mode, // only add the modes when on a system that supports Wow64. static FARPROC wow64p = GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process"); - if (wow64p == nullptr) { + if (!wow64p) { return mode; } @@ -1356,6 +1252,75 @@ bool SystemTools::DeleteRegistryValue(const std::string&, KeyWOW64) } #endif +#ifdef _WIN32 +SystemTools::WindowsFileId::WindowsFileId(unsigned long volumeSerialNumber, + unsigned long fileIndexHigh, + unsigned long fileIndexLow) + : m_volumeSerialNumber(volumeSerialNumber) + , m_fileIndexHigh(fileIndexHigh) + , m_fileIndexLow(fileIndexLow) +{ +} + +bool SystemTools::WindowsFileId::operator==(const WindowsFileId& o) const +{ + return (m_volumeSerialNumber == o.m_volumeSerialNumber && + m_fileIndexHigh == o.m_fileIndexHigh && + m_fileIndexLow == o.m_fileIndexLow); +} + +bool SystemTools::WindowsFileId::operator!=(const WindowsFileId& o) const +{ + return !(*this == o); +} +#else +SystemTools::UnixFileId::UnixFileId(dev_t volumeSerialNumber, + ino_t fileSerialNumber, off_t fileSize) + : m_volumeSerialNumber(volumeSerialNumber) + , m_fileSerialNumber(fileSerialNumber) + , m_fileSize(fileSize) +{ +} + +bool SystemTools::UnixFileId::operator==(const UnixFileId& o) const +{ + return (m_volumeSerialNumber == o.m_volumeSerialNumber && + m_fileSerialNumber == o.m_fileSerialNumber && + m_fileSize == o.m_fileSize); +} + +bool SystemTools::UnixFileId::operator!=(const UnixFileId& o) const +{ + return !(*this == o); +} +#endif + +bool SystemTools::GetFileId(const std::string& file, FileId& id) +{ +#ifdef _WIN32 + HANDLE hFile = + CreateFileW(Encoding::ToWide(file).c_str(), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + BY_HANDLE_FILE_INFORMATION fiBuf; + GetFileInformationByHandle(hFile, &fiBuf); + CloseHandle(hFile); + id = FileId(fiBuf.dwVolumeSerialNumber, fiBuf.nFileIndexHigh, + fiBuf.nFileIndexLow); + return true; + } else { + return false; + } +#else + struct stat fileStat; + if (stat(file.c_str(), &fileStat) == 0) { + id = FileId(fileStat.st_dev, fileStat.st_ino, fileStat.st_size); + return true; + } + return false; +#endif +} + bool SystemTools::SameFile(const std::string& file1, const std::string& file2) { #ifdef _WIN32 @@ -2528,8 +2493,12 @@ SystemTools::CopyStatus SystemTools::CloneFileContent( // NOTE: we cannot use `clonefile` as the {a,c,m}time for the file needs to // be updated by `copy_file_if_different` and `copy_file`. + // These flags are meant to be COPYFILE_METADATA | COPYFILE_CLONE, but CLONE + // forces COPYFILE_NOFOLLOW_SRC and that violates the invariant that this + // should result in a file. if (copyfile(source.c_str(), destination.c_str(), nullptr, - COPYFILE_METADATA | COPYFILE_CLONE) < 0) { + COPYFILE_METADATA | COPYFILE_EXCL | COPYFILE_STAT | + COPYFILE_XATTR | COPYFILE_DATA) < 0) { return CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath }; } # if KWSYS_CXX_HAS_UTIMENSAT @@ -3325,7 +3294,7 @@ Status SystemTools::ReadSymlink(std::string const& newName, // terminated by an empty string (0-0). We need the third string. size_t destLen; substituteNameData = GetAppExecLink(data, destLen); - if (substituteNameData == nullptr || destLen == 0) { + if (!substituteNameData || destLen == 0) { return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED); } substituteNameLength = static_cast(destLen); @@ -3334,6 +3303,14 @@ Status SystemTools::ReadSymlink(std::string const& newName, } std::wstring substituteName(substituteNameData, substituteNameLength); origName = Encoding::ToNarrow(substituteName); + // Symbolic links to absolute paths may use a NT Object Path prefix. + // If the path begins with "\??\UNC\", replace it with "\\". + // Otherwise, if the path begins with "\??\", remove the prefix. + if (origName.compare(0, 8, "\\??\\UNC\\") == 0) { + origName.erase(1, 6); + } else if (origName.compare(0, 4, "\\??\\") == 0) { + origName.erase(0, 4); + } #else char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1]; int count = static_cast( @@ -3428,72 +3405,6 @@ bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut, return true; } -#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP -void SystemTools::AddTranslationPath(const std::string& a, - const std::string& b) -{ - std::string path_a = a; - std::string path_b = b; - SystemTools::ConvertToUnixSlashes(path_a); - SystemTools::ConvertToUnixSlashes(path_b); - // First check this is a directory path, since we don't want the table to - // grow too fat - if (SystemTools::FileIsDirectory(path_a)) { - // Make sure the path is a full path and does not contain no '..' - // Ken--the following code is incorrect. .. can be in a valid path - // for example /home/martink/MyHubba...Hubba/Src - if (SystemTools::FileIsFullPath(path_b) && - path_b.find("..") == std::string::npos) { - // Before inserting make sure path ends with '/' - if (!path_a.empty() && path_a.back() != '/') { - path_a += '/'; - } - if (!path_b.empty() && path_b.back() != '/') { - path_b += '/'; - } - if (!(path_a == path_b)) { - SystemToolsStatics->TranslationMap.insert( - SystemToolsStatic::StringMap::value_type(std::move(path_a), - std::move(path_b))); - } - } - } -} - -void SystemTools::AddKeepPath(const std::string& dir) -{ - std::string cdir; - Realpath(SystemTools::CollapseFullPath(dir), cdir); - SystemTools::AddTranslationPath(cdir, dir); -} - -void SystemTools::CheckTranslationPath(std::string& path) -{ - // Do not translate paths that are too short to have meaningful - // translations. - if (path.size() < 2) { - return; - } - - // Always add a trailing slash before translation. It does not - // matter if this adds an extra slash, but we do not want to - // translate part of a directory (like the foo part of foo-dir). - path += '/'; - - // In case a file was specified we still have to go through this: - // Now convert any path found in the table back to the one desired: - for (auto const& pair : SystemToolsStatics->TranslationMap) { - // We need to check of the path is a substring of the other path - if (path.compare(0, pair.first.size(), pair.first) == 0) { - path = path.replace(0, pair.first.size(), pair.second); - } - } - - // Remove the trailing slash we added before. - path.pop_back(); -} -#endif - static void SystemToolsAppendComponents( std::vector& out_components, std::vector::iterator first, @@ -3556,25 +3467,7 @@ std::string CollapseFullPathImpl(std::string const& in_path, // Transform the path back to a string. std::string newPath = SystemTools::JoinPath(out_components); -#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP - // Update the translation table with this potentially new path. I am not - // sure why this line is here, it seems really questionable, but yet I - // would put good money that if I remove it something will break, basically - // from what I can see it created a mapping from the collapsed path, to be - // replaced by the input path, which almost completely does the opposite of - // this function, the only thing preventing this from happening a lot is - // that if the in_path has a .. in it, then it is not added to the - // translation table. So for most calls this either does nothing due to the - // .. or it adds a translation between identical paths as nothing was - // collapsed, so I am going to try to comment it out, and see what hits the - // fan, hopefully quickly. - // Commented out line below: - // SystemTools::AddTranslationPath(newPath, in_path); - - SystemTools::CheckTranslationPath(newPath); -#endif #ifdef _WIN32 - newPath = SystemToolsStatics->GetActualCaseForPathCached(newPath); SystemTools::ConvertToUnixSlashes(newPath); #endif // Return the reconstructed path. @@ -3683,7 +3576,7 @@ std::string SystemTools::RelativePath(const std::string& local, std::string SystemTools::GetActualCaseForPath(const std::string& p) { #ifdef _WIN32 - return SystemToolsStatic::GetCasePathName(p, false); + return SystemToolsStatic::GetCasePathName(p); #else return p; #endif @@ -4895,51 +4788,6 @@ void SystemTools::ClassInitialize() // Create statics singleton instance SystemToolsStatics = new SystemToolsStatic; - -#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP -// Add some special translation paths for unix. These are not added -// for windows because drive letters need to be maintained. Also, -// there are not sym-links and mount points on windows anyway. -# if !defined(_WIN32) || defined(__CYGWIN__) - // The tmp path is frequently a logical path so always keep it: - SystemTools::AddKeepPath("/tmp/"); - - // If the current working directory is a logical path then keep the - // logical name. - std::string pwd_str; - if (SystemTools::GetEnv("PWD", pwd_str)) { - char buf[2048]; - if (const char* cwd = Getcwd(buf, 2048)) { - // The current working directory may be a logical path. Find - // the shortest logical path that still produces the correct - // physical path. - std::string cwd_changed; - std::string pwd_changed; - - // Test progressively shorter logical-to-physical mappings. - std::string cwd_str = cwd; - std::string pwd_path; - Realpath(pwd_str, pwd_path); - while (cwd_str == pwd_path && cwd_str != pwd_str) { - // The current pair of paths is a working logical mapping. - cwd_changed = cwd_str; - pwd_changed = pwd_str; - - // Strip off one directory level and see if the logical - // mapping still works. - pwd_str = SystemTools::GetFilenamePath(pwd_str); - cwd_str = SystemTools::GetFilenamePath(cwd_str); - Realpath(pwd_str, pwd_path); - } - - // Add the translation to keep the logical path name. - if (!cwd_changed.empty() && !pwd_changed.empty()) { - SystemTools::AddTranslationPath(cwd_changed, pwd_changed); - } - } - } -# endif -#endif } void SystemTools::ClassFinalize() diff --git a/SystemTools.hxx.in b/SystemTools.hxx.in index 729928e6f3a..03fa3bced07 100644 --- a/SystemTools.hxx.in +++ b/SystemTools.hxx.in @@ -616,6 +616,52 @@ public: static CopyStatus CloneFileContent(std::string const& source, std::string const& destination); + /** + * Object encapsulating a unique identifier for a file + * or directory + */ +#ifdef _WIN32 + class WindowsFileId + { + public: + WindowsFileId() = default; + WindowsFileId(unsigned long volumeSerialNumber, + unsigned long fileIndexHigh, unsigned long fileIndexLow); + + bool operator==(const WindowsFileId& o) const; + bool operator!=(const WindowsFileId& o) const; + + private: + unsigned long m_volumeSerialNumber; + unsigned long m_fileIndexHigh; + unsigned long m_fileIndexLow; + }; + using FileId = WindowsFileId; +#else + class UnixFileId + { + public: + UnixFileId() = default; + UnixFileId(dev_t volumeSerialNumber, ino_t fileSerialNumber, + off_t fileSize); + + bool operator==(const UnixFileId& o) const; + bool operator!=(const UnixFileId& o) const; + + private: + dev_t m_volumeSerialNumber; + ino_t m_fileSerialNumber; + off_t m_fileSize; + }; + using FileId = UnixFileId; +#endif + + /** + * Outputs a FileId for the given file or directory. + * Returns true on success, false on failure + */ + static bool GetFileId(const std::string& file, FileId& id); + /** * Return true if the two files are the same file */ @@ -938,25 +984,6 @@ public: */ static int GetTerminalWidth(); -#if @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP - /** - * Add an entry in the path translation table. - */ - static void AddTranslationPath(const std::string& dir, - const std::string& refdir); - - /** - * If dir is different after CollapseFullPath is called, - * Then insert it into the path translation table - */ - static void AddKeepPath(const std::string& dir); - - /** - * Update path by going through the Path Translation table; - */ - static void CheckTranslationPath(std::string& path); -#endif - /** * Delay the execution for a specified amount of time specified * in milliseconds @@ -1006,14 +1033,7 @@ public: static std::string DecodeURL(const std::string& url); private: - /** - * Allocate the stl map that serve as the Path Translation table. - */ static void ClassInitialize(); - - /** - * Deallocate the stl map that serve as the Path Translation table. - */ static void ClassFinalize(); /** diff --git a/testConsoleBuf.cxx b/testConsoleBuf.cxx index f9b826d2152..64c61e2ff1f 100644 --- a/testConsoleBuf.cxx +++ b/testConsoleBuf.cxx @@ -476,6 +476,22 @@ static int testFile() # define _WIN32_WINNT_VISTA 0x0600 # endif +static bool consoleIsConhost() +{ + wchar_t consoleClassNameBuf[64]; + int const consoleClassNameLen = GetClassNameW( + GetConsoleWindow(), &consoleClassNameBuf[0], sizeof(consoleClassNameBuf)); + // Windows Console Host: ConsoleWindowClass + // Windows Terminal / ConPTY: PseudoConsoleWindow (undocumented) + return (consoleClassNameLen > 0 && + wcscmp(consoleClassNameBuf, L"ConsoleWindowClass") == 0); +} + +static bool charIsNUL(wchar_t c) +{ + return c == 0; +} + static int testConsole() { int didFail = 1; @@ -691,7 +707,15 @@ static int testConsole() throw std::runtime_error("ReadConsoleOutputCharacter failed!"); } std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString); - std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' '); + if (consoleIsConhost()) { + // Windows Console Host converts NUL bytes to spaces. + std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' '); + } else { + // Windows Terminal / ConPTY removes NUL bytes. + wideTestString.erase(std::remove_if(wideTestString.begin(), + wideTestString.end(), charIsNUL), + wideTestString.end()); + } std::wstring wideInputTestString = kwsys::Encoding::ToWide(encodedInputTestString); if (memcmp(outputBuffer, wideTestString.c_str(),