| /* 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 <cerrno> | |
| #include <clocale> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <iostream> | |
| #include <fcntl.h> | |
| #ifdef _WIN32 | |
| # include <windows.h> | |
| # include <io.h> // for _close, _dup2, _get_osfhandle | |
| # include "cm_fileno.hxx" | |
| #else | |
| # include <cm/string_view> | |
| # include <cmext/string_view> | |
| # include <unistd.h> | |
| #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<HANDLE>(_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 ".<code-page>" 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; | |
| } | |
| } | |
| } |