Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmStdIoInit.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmStdIoInit.h"
4
5
#include <cerrno>
6
#include <clocale>
7
#include <cstdio>
8
#include <cstdlib>
9
#include <iostream>
10
11
#include <fcntl.h>
12
13
#ifdef _WIN32
14
#  include <windows.h>
15
16
#  include <io.h> // for _close, _dup2, _get_osfhandle
17
18
#  include "cm_fileno.hxx"
19
#else
20
#  include <cm/string_view>
21
#  include <cmext/string_view>
22
23
#  include <unistd.h>
24
#endif
25
26
#include "cmStdIoStream.h"
27
28
namespace cm {
29
namespace StdIo {
30
31
namespace {
32
33
#ifdef _WIN32
34
void InitStdPipe(int stdFd, DWORD nStdHandle, FILE* stream,
35
                 wchar_t const* mode)
36
{
37
  if (cm_fileno(stream) >= 0) {
38
    return;
39
  }
40
  _close(stdFd);
41
  _wfreopen(L"NUL", mode, stream);
42
  int fd = cm_fileno(stream);
43
  if (fd < 0) {
44
    perror("failed to open NUL for missing stdio pipe");
45
    abort();
46
  }
47
  if (fd != stdFd) {
48
    _dup2(fd, stdFd);
49
  }
50
  SetStdHandle(nStdHandle, reinterpret_cast<HANDLE>(_get_osfhandle(fd)));
51
}
52
#else
53
void InitStdPipe(int fd)
54
0
{
55
0
  if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
56
0
    return;
57
0
  }
58
59
0
  int f = open("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY);
60
0
  if (f == -1) {
61
0
    perror("failed to open /dev/null for missing stdio pipe");
62
0
    abort();
63
0
  }
64
0
  if (f != fd) {
65
0
    dup2(f, fd);
66
0
    close(f);
67
0
  }
68
0
}
69
#endif
70
71
struct InitStdPipes
72
{
73
  InitStdPipes()
74
0
  {
75
#ifdef _WIN32
76
    InitStdPipe(0, STD_INPUT_HANDLE, stdin, L"rb");
77
    InitStdPipe(1, STD_OUTPUT_HANDLE, stdout, L"wb");
78
    InitStdPipe(2, STD_ERROR_HANDLE, stderr, L"wb");
79
#else
80
0
    InitStdPipe(STDIN_FILENO);
81
0
    InitStdPipe(STDOUT_FILENO);
82
0
    InitStdPipe(STDERR_FILENO);
83
0
#endif
84
0
  }
85
};
86
87
} // anonymous namespace
88
89
class Globals
90
{
91
public:
92
  std::ios::Init InitIos;
93
  InitStdPipes InitPipes;
94
  IStream StdIn{ std::cin, stdin };
95
  OStream StdOut{ std::cout, stdout };
96
  OStream StdErr{ std::cerr, stderr };
97
98
  Globals();
99
100
#ifdef _WIN32
101
  static BOOL WINAPI CtrlHandler(DWORD /*dwCtrlType*/)
102
  {
103
    Get().StdErr.Destroy();
104
    Get().StdOut.Destroy();
105
    Get().StdIn.Destroy();
106
    return FALSE;
107
  }
108
#endif
109
110
  static Globals& Get();
111
};
112
113
Globals::Globals()
114
0
{
115
#ifdef _WIN32
116
  // On Windows, setlocale offers a ".<code-page>" syntax to select the
117
  // user's locale with a specific character set.  We always use UTF-8.
118
  std::setlocale(LC_CTYPE, ".UTF-8");
119
120
  SetConsoleCtrlHandler(CtrlHandler, TRUE);
121
#else
122
  // On non-Windows platforms, we select the user's locale.
123
0
  std::setlocale(LC_CTYPE, "");
124
125
  // In the "C" locale try switching to a UTF-8 character set.
126
0
  if (char const* locale = std::setlocale(LC_CTYPE, nullptr)) {
127
0
    if (locale == "C"_s || locale == "POSIX"_s) {
128
0
      std::setlocale(LC_CTYPE, "C.UTF-8")
129
#  ifdef __APPLE__
130
        || std::setlocale(LC_CTYPE, "UTF-8")
131
#  endif
132
0
        || std::setlocale(LC_CTYPE, "en_US.UTF-8");
133
0
    }
134
0
  }
135
0
#endif
136
0
}
137
138
Globals& Globals::Get()
139
0
{
140
0
  static Globals globals;
141
0
  return globals;
142
0
}
143
144
Init::Init()
145
0
{
146
0
  Globals::Get();
147
0
}
148
149
IStream& In()
150
0
{
151
0
  return Globals::Get().StdIn;
152
0
}
153
154
OStream& Out()
155
0
{
156
0
  return Globals::Get().StdOut;
157
0
}
158
159
OStream& Err()
160
0
{
161
0
  return Globals::Get().StdErr;
162
0
}
163
164
}
165
}