mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-12 22:17:06 +00:00
The UTF-8 charset is a superset of the C locale's 7-bit ASCII charset.
Since commit d284ce2fd6 (Initialize character set encoding on program
startup, 2026-02-02, v4.3.0-rc1~40^2) we set a single locale globally.
We can handle archives with UTF-8 paths by running in a locale with a
UTF-8 charset.
Fixes: #25226
Issue: #26903
Issue: #27562
166 lines
3.1 KiB
C++
166 lines
3.1 KiB
C++
/* 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;
|
|
}
|
|
|
|
}
|
|
}
|