/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmStdIoInit.h" #include #include #include #include #include #include #ifdef _WIN32 # include # include // for _close, _dup2, _get_osfhandle # include "cm_fileno.hxx" #else # include # include # include #endif #include "cmStdIoStream.h" namespace cm { namespace StdIo { namespace { #ifdef _WIN32 void InitStdPipe(int stdFd, DWORD nStdHandle, FILE* stream, wchar_t const* mode) { if (cm_fileno(stream) >= 0) { return; } _close(stdFd); _wfreopen(L"NUL", mode, stream); int fd = cm_fileno(stream); if (fd < 0) { perror("failed to open NUL for missing stdio pipe"); abort(); } if (fd != stdFd) { _dup2(fd, stdFd); } SetStdHandle(nStdHandle, reinterpret_cast(_get_osfhandle(fd))); } #else void InitStdPipe(int fd) { if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) { return; } int f = open("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY); if (f == -1) { perror("failed to open /dev/null for missing stdio pipe"); abort(); } if (f != fd) { dup2(f, fd); close(f); } } #endif struct InitStdPipes { InitStdPipes() { #ifdef _WIN32 InitStdPipe(0, STD_INPUT_HANDLE, stdin, L"rb"); InitStdPipe(1, STD_OUTPUT_HANDLE, stdout, L"wb"); InitStdPipe(2, STD_ERROR_HANDLE, stderr, L"wb"); #else InitStdPipe(STDIN_FILENO); InitStdPipe(STDOUT_FILENO); InitStdPipe(STDERR_FILENO); #endif } }; } // anonymous namespace class Globals { public: std::ios::Init InitIos; InitStdPipes InitPipes; IStream StdIn{ std::cin, stdin }; OStream StdOut{ std::cout, stdout }; OStream StdErr{ std::cerr, stderr }; Globals(); #ifdef _WIN32 static BOOL WINAPI CtrlHandler(DWORD /*dwCtrlType*/) { Get().StdErr.Destroy(); Get().StdOut.Destroy(); Get().StdIn.Destroy(); return FALSE; } #endif static Globals& Get(); }; Globals::Globals() { #ifdef _WIN32 // On Windows, setlocale offers a "." syntax to select the // user's locale with a specific character set. We always use UTF-8. std::setlocale(LC_CTYPE, ".UTF-8"); SetConsoleCtrlHandler(CtrlHandler, TRUE); #else // On non-Windows platforms, we select the user's locale. std::setlocale(LC_CTYPE, ""); // In the "C" locale try switching to a UTF-8 character set. if (char const* locale = std::setlocale(LC_CTYPE, nullptr)) { if (locale == "C"_s || locale == "POSIX"_s) { std::setlocale(LC_CTYPE, "C.UTF-8") # ifdef __APPLE__ || std::setlocale(LC_CTYPE, "UTF-8") # endif || std::setlocale(LC_CTYPE, "en_US.UTF-8"); } } #endif } Globals& Globals::Get() { static Globals globals; return globals; } Init::Init() { Globals::Get(); } IStream& In() { return Globals::Get().StdIn; } OStream& Out() { return Globals::Get().StdOut; } OStream& Err() { return Globals::Get().StdErr; } } }