Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratedFileStream.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 "cmGeneratedFileStream.h"
4
5
#include <cstdio>
6
#include <locale>
7
8
#include "cmStringAlgorithms.h"
9
#include "cmSystemTools.h"
10
11
#if !defined(CMAKE_BOOTSTRAP)
12
#  include <cm3p/zlib.h>
13
14
#  include "cm_codecvt.hxx"
15
#endif
16
17
cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding)
18
19.9k
{
19
19.9k
#ifndef CMAKE_BOOTSTRAP
20
19.9k
  if (encoding != codecvt_Encoding::None) {
21
0
    this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
22
0
  }
23
#else
24
  static_cast<void>(encoding);
25
#endif
26
19.9k
}
Unexecuted instantiation: cmGeneratedFileStream::cmGeneratedFileStream(codecvt_Encoding)
cmGeneratedFileStream::cmGeneratedFileStream(codecvt_Encoding)
Line
Count
Source
18
19.9k
{
19
19.9k
#ifndef CMAKE_BOOTSTRAP
20
19.9k
  if (encoding != codecvt_Encoding::None) {
21
0
    this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
22
0
  }
23
#else
24
  static_cast<void>(encoding);
25
#endif
26
19.9k
}
27
28
cmGeneratedFileStream::cmGeneratedFileStream(std::string const& name,
29
                                             bool quiet, Encoding encoding)
30
0
  : cmGeneratedFileStreamBase(name)
31
0
  , Stream(this->TempName.c_str()) // NOLINT(cmake-use-cmsys-fstream)
32
0
{
33
  // Check if the file opened.
34
0
  if (!*this && !quiet) {
35
0
    cmSystemTools::Error("Cannot open file for write: " + this->TempName);
36
0
    cmSystemTools::ReportLastSystemError("");
37
0
  }
38
0
#ifndef CMAKE_BOOTSTRAP
39
0
  if (encoding != codecvt_Encoding::None) {
40
0
    this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
41
0
  }
42
#else
43
  static_cast<void>(encoding);
44
#endif
45
0
  if (encoding == codecvt_Encoding::UTF8_WITH_BOM) {
46
    // Write the BOM encoding header into the file
47
0
    char magic[] = { static_cast<char>(0xEF), static_cast<char>(0xBB),
48
0
                     static_cast<char>(0xBF) };
49
0
    this->write(magic, 3);
50
0
  }
51
0
}
Unexecuted instantiation: cmGeneratedFileStream::cmGeneratedFileStream(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, codecvt_Encoding)
Unexecuted instantiation: cmGeneratedFileStream::cmGeneratedFileStream(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, codecvt_Encoding)
52
53
cmGeneratedFileStream::~cmGeneratedFileStream()
54
19.9k
{
55
  // This is the first destructor called.  Check the status of the
56
  // stream and give the information to the private base.  Next the
57
  // stream will be destroyed which will close the temporary file.
58
  // Finally the base destructor will be called to replace the
59
  // destination file.
60
19.9k
  this->Okay = !this->fail();
61
19.9k
}
62
63
cmGeneratedFileStream& cmGeneratedFileStream::Open(std::string const& name,
64
                                                   bool quiet, bool binaryFlag)
65
0
{
66
  // Store the file name and construct the temporary file name.
67
0
  this->cmGeneratedFileStreamBase::Open(name);
68
69
  // Open the temporary output file.
70
0
  if (binaryFlag) {
71
0
    this->Stream::open( // NOLINT(cmake-use-cmsys-fstream)
72
0
      this->TempName.c_str(), std::ios::out | std::ios::binary);
73
0
  } else {
74
0
    this->Stream::open( // NOLINT(cmake-use-cmsys-fstream)
75
0
      this->TempName.c_str());
76
0
  }
77
78
  // Check if the file opened.
79
0
  if (!*this && !quiet) {
80
0
    cmSystemTools::Error("Cannot open file for write: " + this->TempName);
81
0
    cmSystemTools::ReportLastSystemError("");
82
0
  }
83
0
  return *this;
84
0
}
85
86
bool cmGeneratedFileStream::Close()
87
0
{
88
  // Save whether the temporary output file is valid before closing.
89
0
  this->Okay = !this->fail();
90
91
  // Close the temporary output file.
92
0
  this->Stream::close(); // NOLINT(cmake-use-cmsys-fstream)
93
94
  // Remove the temporary file (possibly by renaming to the real file).
95
0
  return this->cmGeneratedFileStreamBase::Close();
96
0
}
97
98
bool cmGeneratedFileStream::WriteIfDifferent(std::string const& name,
99
                                             cm::string_view content,
100
                                             bool quiet, bool* written)
101
0
{
102
0
  if (written) {
103
0
    *written = false;
104
0
  }
105
0
  if (cmSystemTools::FileHasContent(name, content)) {
106
0
    return true;
107
0
  }
108
0
  cmGeneratedFileStream fout;
109
0
  fout.Open(name, quiet, true);
110
0
  if (!fout) {
111
0
    return false;
112
0
  }
113
0
  fout.write(content.data(), static_cast<std::streamsize>(content.size()));
114
0
  if (!fout.Close()) {
115
0
    return false;
116
0
  }
117
0
  if (written) {
118
0
    *written = true;
119
0
  }
120
0
  return true;
121
0
}
122
123
void cmGeneratedFileStream::SetCopyIfDifferent(bool copy_if_different)
124
0
{
125
0
  this->CopyIfDifferent = copy_if_different;
126
0
}
127
128
void cmGeneratedFileStream::SetCompression(bool compression)
129
0
{
130
0
  this->Compress = compression;
131
0
}
132
133
void cmGeneratedFileStream::SetCompressionExtraExtension(bool ext)
134
0
{
135
0
  this->CompressExtraExtension = ext;
136
0
}
137
138
19.9k
cmGeneratedFileStreamBase::cmGeneratedFileStreamBase() = default;
139
140
cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(std::string const& name)
141
0
{
142
0
  this->Open(name);
143
0
}
144
145
cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
146
19.9k
{
147
19.9k
  this->Close();
148
19.9k
}
149
150
void cmGeneratedFileStreamBase::Open(std::string const& name)
151
0
{
152
  // Save the original name of the file.
153
0
  this->Name = cmSystemTools::CollapseFullPath(name);
154
155
  // Create the name of the temporary file.
156
0
  this->TempName = this->Name;
157
#if defined(__VMS)
158
  this->TempName += "_";
159
#else
160
0
  this->TempName += ".";
161
0
#endif
162
0
  if (!this->TempExt.empty()) {
163
0
    this->TempName += this->TempExt;
164
0
  } else {
165
0
    char buf[64];
166
0
    snprintf(buf, sizeof(buf), "tmp%05x",
167
0
             cmSystemTools::RandomNumber() & 0xFFFFF);
168
0
    this->TempName += buf;
169
0
  }
170
171
  // Make sure the temporary file that will be used is not present.
172
0
  cmSystemTools::RemoveFile(this->TempName);
173
174
0
  std::string dir = cmSystemTools::GetFilenamePath(this->TempName);
175
0
  cmSystemTools::MakeDirectory(dir);
176
0
}
177
178
bool cmGeneratedFileStreamBase::Close()
179
19.9k
{
180
19.9k
  bool replaced = false;
181
182
19.9k
  std::string resname = this->Name;
183
19.9k
  if (this->Compress && this->CompressExtraExtension) {
184
0
    resname += ".gz";
185
0
  }
186
187
  // Only consider replacing the destination file if no error
188
  // occurred.
189
19.9k
  if (!this->Name.empty() && this->Okay &&
190
0
      (!this->CopyIfDifferent ||
191
0
       cmSystemTools::FilesDiffer(this->TempName, resname))) {
192
    // The destination is to be replaced.  Rename the temporary to the
193
    // destination atomically.
194
0
    if (this->Compress) {
195
0
      std::string gzname = cmStrCat(this->TempName, ".temp.gz");
196
0
      if (this->CompressFile(this->TempName, gzname)) {
197
0
        this->RenameFile(gzname, resname);
198
0
      }
199
0
      cmSystemTools::RemoveFile(gzname);
200
0
    } else {
201
0
      this->RenameFile(this->TempName, resname);
202
0
    }
203
204
0
    replaced = true;
205
0
  }
206
207
  // Else, the destination was not replaced.
208
  //
209
  // Always delete the temporary file. We never want it to stay around.
210
19.9k
  if (!this->TempName.empty()) {
211
0
    cmSystemTools::RemoveFile(this->TempName);
212
0
  }
213
214
19.9k
  return replaced;
215
19.9k
}
216
217
#ifndef CMAKE_BOOTSTRAP
218
int cmGeneratedFileStreamBase::CompressFile(std::string const& oldname,
219
                                            std::string const& newname)
220
0
{
221
0
  gzFile gf = gzopen(newname.c_str(), "w");
222
0
  if (!gf) {
223
0
    return 0;
224
0
  }
225
0
  FILE* ifs = cmsys::SystemTools::Fopen(oldname, "r");
226
0
  if (!ifs) {
227
0
    gzclose(gf);
228
0
    return 0;
229
0
  }
230
0
  size_t res;
231
0
  size_t const BUFFER_SIZE = 1024;
232
0
  char buffer[BUFFER_SIZE];
233
0
  while ((res = fread(buffer, 1, BUFFER_SIZE, ifs)) > 0) {
234
0
    if (!gzwrite(gf, buffer, static_cast<int>(res))) {
235
0
      fclose(ifs);
236
0
      gzclose(gf);
237
0
      return 0;
238
0
    }
239
0
  }
240
0
  fclose(ifs);
241
0
  gzclose(gf);
242
0
  return 1;
243
0
}
244
#else
245
int cmGeneratedFileStreamBase::CompressFile(std::string const&,
246
                                            std::string const&)
247
{
248
  return 0;
249
}
250
#endif
251
252
int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname,
253
                                          std::string const& newname)
254
0
{
255
0
  return cmSystemTools::RenameFile(oldname, newname);
256
0
}
257
258
void cmGeneratedFileStream::SetName(std::string const& fname)
259
0
{
260
0
  this->Name = cmSystemTools::CollapseFullPath(fname);
261
0
}
262
263
void cmGeneratedFileStream::SetTempExt(std::string const& ext)
264
0
{
265
0
  this->TempExt = ext;
266
0
}
267
268
void cmGeneratedFileStream::WriteAltEncoding(std::string const& data,
269
                                             Encoding encoding)
270
0
{
271
0
#ifndef CMAKE_BOOTSTRAP
272
0
  std::locale prevLocale =
273
0
    this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
274
0
  this->write(data.data(), data.size());
275
0
  this->imbue(prevLocale);
276
#else
277
  static_cast<void>(encoding);
278
  this->write(data.data(), data.size());
279
#endif
280
0
}