Coverage Report

Created: 2026-02-09 06:05

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
35
{
19
35
#ifndef CMAKE_BOOTSTRAP
20
35
  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
35
}
Unexecuted instantiation: cmGeneratedFileStream::cmGeneratedFileStream(codecvt_Encoding)
cmGeneratedFileStream::cmGeneratedFileStream(codecvt_Encoding)
Line
Count
Source
18
35
{
19
35
#ifndef CMAKE_BOOTSTRAP
20
35
  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
35
}
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
35
{
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
35
  this->Okay = !this->fail();
61
35
}
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
void cmGeneratedFileStream::SetCopyIfDifferent(bool copy_if_different)
99
0
{
100
0
  this->CopyIfDifferent = copy_if_different;
101
0
}
102
103
void cmGeneratedFileStream::SetCompression(bool compression)
104
0
{
105
0
  this->Compress = compression;
106
0
}
107
108
void cmGeneratedFileStream::SetCompressionExtraExtension(bool ext)
109
0
{
110
0
  this->CompressExtraExtension = ext;
111
0
}
112
113
35
cmGeneratedFileStreamBase::cmGeneratedFileStreamBase() = default;
114
115
cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(std::string const& name)
116
0
{
117
0
  this->Open(name);
118
0
}
119
120
cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
121
35
{
122
35
  this->Close();
123
35
}
124
125
void cmGeneratedFileStreamBase::Open(std::string const& name)
126
0
{
127
  // Save the original name of the file.
128
0
  this->Name = cmSystemTools::CollapseFullPath(name);
129
130
  // Create the name of the temporary file.
131
0
  this->TempName = this->Name;
132
#if defined(__VMS)
133
  this->TempName += "_";
134
#else
135
0
  this->TempName += ".";
136
0
#endif
137
0
  if (!this->TempExt.empty()) {
138
0
    this->TempName += this->TempExt;
139
0
  } else {
140
0
    char buf[64];
141
0
    snprintf(buf, sizeof(buf), "tmp%05x",
142
0
             cmSystemTools::RandomNumber() & 0xFFFFF);
143
0
    this->TempName += buf;
144
0
  }
145
146
  // Make sure the temporary file that will be used is not present.
147
0
  cmSystemTools::RemoveFile(this->TempName);
148
149
0
  std::string dir = cmSystemTools::GetFilenamePath(this->TempName);
150
0
  cmSystemTools::MakeDirectory(dir);
151
0
}
152
153
bool cmGeneratedFileStreamBase::Close()
154
35
{
155
35
  bool replaced = false;
156
157
35
  std::string resname = this->Name;
158
35
  if (this->Compress && this->CompressExtraExtension) {
159
0
    resname += ".gz";
160
0
  }
161
162
  // Only consider replacing the destination file if no error
163
  // occurred.
164
35
  if (!this->Name.empty() && this->Okay &&
165
0
      (!this->CopyIfDifferent ||
166
0
       cmSystemTools::FilesDiffer(this->TempName, resname))) {
167
    // The destination is to be replaced.  Rename the temporary to the
168
    // destination atomically.
169
0
    if (this->Compress) {
170
0
      std::string gzname = cmStrCat(this->TempName, ".temp.gz");
171
0
      if (this->CompressFile(this->TempName, gzname)) {
172
0
        this->RenameFile(gzname, resname);
173
0
      }
174
0
      cmSystemTools::RemoveFile(gzname);
175
0
    } else {
176
0
      this->RenameFile(this->TempName, resname);
177
0
    }
178
179
0
    replaced = true;
180
0
  }
181
182
  // Else, the destination was not replaced.
183
  //
184
  // Always delete the temporary file. We never want it to stay around.
185
35
  if (!this->TempName.empty()) {
186
0
    cmSystemTools::RemoveFile(this->TempName);
187
0
  }
188
189
35
  return replaced;
190
35
}
191
192
#ifndef CMAKE_BOOTSTRAP
193
int cmGeneratedFileStreamBase::CompressFile(std::string const& oldname,
194
                                            std::string const& newname)
195
0
{
196
0
  gzFile gf = gzopen(newname.c_str(), "w");
197
0
  if (!gf) {
198
0
    return 0;
199
0
  }
200
0
  FILE* ifs = cmsys::SystemTools::Fopen(oldname, "r");
201
0
  if (!ifs) {
202
0
    gzclose(gf);
203
0
    return 0;
204
0
  }
205
0
  size_t res;
206
0
  size_t const BUFFER_SIZE = 1024;
207
0
  char buffer[BUFFER_SIZE];
208
0
  while ((res = fread(buffer, 1, BUFFER_SIZE, ifs)) > 0) {
209
0
    if (!gzwrite(gf, buffer, static_cast<int>(res))) {
210
0
      fclose(ifs);
211
0
      gzclose(gf);
212
0
      return 0;
213
0
    }
214
0
  }
215
0
  fclose(ifs);
216
0
  gzclose(gf);
217
0
  return 1;
218
0
}
219
#else
220
int cmGeneratedFileStreamBase::CompressFile(std::string const&,
221
                                            std::string const&)
222
{
223
  return 0;
224
}
225
#endif
226
227
int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname,
228
                                          std::string const& newname)
229
0
{
230
0
  return cmSystemTools::RenameFile(oldname, newname);
231
0
}
232
233
void cmGeneratedFileStream::SetName(std::string const& fname)
234
0
{
235
0
  this->Name = cmSystemTools::CollapseFullPath(fname);
236
0
}
237
238
void cmGeneratedFileStream::SetTempExt(std::string const& ext)
239
0
{
240
0
  this->TempExt = ext;
241
0
}
242
243
void cmGeneratedFileStream::WriteAltEncoding(std::string const& data,
244
                                             Encoding encoding)
245
0
{
246
0
#ifndef CMAKE_BOOTSTRAP
247
0
  std::locale prevLocale =
248
0
    this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
249
0
  this->write(data.data(), data.size());
250
0
  this->imbue(prevLocale);
251
#else
252
  static_cast<void>(encoding);
253
  this->write(data.data(), data.size());
254
#endif
255
0
}