Merge branch 'release-3.29'
[kiteware-cmake.git] / Source / cmGeneratedFileStream.cxx
blob37a2be4a75a1b906cd6532908b616b7fa6780edb
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmGeneratedFileStream.h"
5 #include <cstdio>
6 #include <locale>
8 #include "cmStringAlgorithms.h"
9 #include "cmSystemTools.h"
11 #if !defined(CMAKE_BOOTSTRAP)
12 # include <cm3p/zlib.h>
14 # include "cm_codecvt.hxx"
15 #endif
17 cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding)
19 #ifndef CMAKE_BOOTSTRAP
20 if (encoding != codecvt_Encoding::None) {
21 this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
23 #else
24 static_cast<void>(encoding);
25 #endif
28 cmGeneratedFileStream::cmGeneratedFileStream(std::string const& name,
29 bool quiet, Encoding encoding)
30 : cmGeneratedFileStreamBase(name)
31 , Stream(this->TempName.c_str()) // NOLINT(cmake-use-cmsys-fstream)
33 // Check if the file opened.
34 if (!*this && !quiet) {
35 cmSystemTools::Error("Cannot open file for write: " + this->TempName);
36 cmSystemTools::ReportLastSystemError("");
38 #ifndef CMAKE_BOOTSTRAP
39 if (encoding != codecvt_Encoding::None) {
40 this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
42 #else
43 static_cast<void>(encoding);
44 #endif
45 if (encoding == codecvt_Encoding::UTF8_WITH_BOM) {
46 // Write the BOM encoding header into the file
47 char magic[] = { static_cast<char>(0xEF), static_cast<char>(0xBB),
48 static_cast<char>(0xBF) };
49 this->write(magic, 3);
53 cmGeneratedFileStream::~cmGeneratedFileStream()
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 this->Okay = !this->fail();
63 cmGeneratedFileStream& cmGeneratedFileStream::Open(std::string const& name,
64 bool quiet, bool binaryFlag)
66 // Store the file name and construct the temporary file name.
67 this->cmGeneratedFileStreamBase::Open(name);
69 // Open the temporary output file.
70 if (binaryFlag) {
71 this->Stream::open( // NOLINT(cmake-use-cmsys-fstream)
72 this->TempName.c_str(), std::ios::out | std::ios::binary);
73 } else {
74 this->Stream::open( // NOLINT(cmake-use-cmsys-fstream)
75 this->TempName.c_str());
78 // Check if the file opened.
79 if (!*this && !quiet) {
80 cmSystemTools::Error("Cannot open file for write: " + this->TempName);
81 cmSystemTools::ReportLastSystemError("");
83 return *this;
86 bool cmGeneratedFileStream::Close()
88 // Save whether the temporary output file is valid before closing.
89 this->Okay = !this->fail();
91 // Close the temporary output file.
92 this->Stream::close(); // NOLINT(cmake-use-cmsys-fstream)
94 // Remove the temporary file (possibly by renaming to the real file).
95 return this->cmGeneratedFileStreamBase::Close();
98 void cmGeneratedFileStream::SetCopyIfDifferent(bool copy_if_different)
100 this->CopyIfDifferent = copy_if_different;
103 void cmGeneratedFileStream::SetCompression(bool compression)
105 this->Compress = compression;
108 void cmGeneratedFileStream::SetCompressionExtraExtension(bool ext)
110 this->CompressExtraExtension = ext;
113 cmGeneratedFileStreamBase::cmGeneratedFileStreamBase() = default;
115 cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(std::string const& name)
117 this->Open(name);
120 cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
122 this->Close();
125 void cmGeneratedFileStreamBase::Open(std::string const& name)
127 // Save the original name of the file.
128 this->Name = cmSystemTools::CollapseFullPath(name);
130 // Create the name of the temporary file.
131 this->TempName = this->Name;
132 #if defined(__VMS)
133 this->TempName += "_";
134 #else
135 this->TempName += ".";
136 #endif
137 if (!this->TempExt.empty()) {
138 this->TempName += this->TempExt;
139 } else {
140 char buf[64];
141 snprintf(buf, sizeof(buf), "tmp%05x",
142 cmSystemTools::RandomSeed() & 0xFFFFF);
143 this->TempName += buf;
146 // Make sure the temporary file that will be used is not present.
147 cmSystemTools::RemoveFile(this->TempName);
149 std::string dir = cmSystemTools::GetFilenamePath(this->TempName);
150 cmSystemTools::MakeDirectory(dir);
153 bool cmGeneratedFileStreamBase::Close()
155 bool replaced = false;
157 std::string resname = this->Name;
158 if (this->Compress && this->CompressExtraExtension) {
159 resname += ".gz";
162 // Only consider replacing the destination file if no error
163 // occurred.
164 if (!this->Name.empty() && this->Okay &&
165 (!this->CopyIfDifferent ||
166 cmSystemTools::FilesDiffer(this->TempName, resname))) {
167 // The destination is to be replaced. Rename the temporary to the
168 // destination atomically.
169 if (this->Compress) {
170 std::string gzname = cmStrCat(this->TempName, ".temp.gz");
171 if (this->CompressFile(this->TempName, gzname)) {
172 this->RenameFile(gzname, resname);
174 cmSystemTools::RemoveFile(gzname);
175 } else {
176 this->RenameFile(this->TempName, resname);
179 replaced = true;
182 // Else, the destination was not replaced.
184 // Always delete the temporary file. We never want it to stay around.
185 if (!this->TempName.empty()) {
186 cmSystemTools::RemoveFile(this->TempName);
189 return replaced;
192 #ifndef CMAKE_BOOTSTRAP
193 int cmGeneratedFileStreamBase::CompressFile(std::string const& oldname,
194 std::string const& newname)
196 gzFile gf = gzopen(newname.c_str(), "w");
197 if (!gf) {
198 return 0;
200 FILE* ifs = cmsys::SystemTools::Fopen(oldname, "r");
201 if (!ifs) {
202 gzclose(gf);
203 return 0;
205 size_t res;
206 const size_t BUFFER_SIZE = 1024;
207 char buffer[BUFFER_SIZE];
208 while ((res = fread(buffer, 1, BUFFER_SIZE, ifs)) > 0) {
209 if (!gzwrite(gf, buffer, static_cast<int>(res))) {
210 fclose(ifs);
211 gzclose(gf);
212 return 0;
215 fclose(ifs);
216 gzclose(gf);
217 return 1;
219 #else
220 int cmGeneratedFileStreamBase::CompressFile(std::string const&,
221 std::string const&)
223 return 0;
225 #endif
227 int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname,
228 std::string const& newname)
230 return cmSystemTools::RenameFile(oldname, newname);
233 void cmGeneratedFileStream::SetName(const std::string& fname)
235 this->Name = cmSystemTools::CollapseFullPath(fname);
238 void cmGeneratedFileStream::SetTempExt(std::string const& ext)
240 this->TempExt = ext;
243 void cmGeneratedFileStream::WriteAltEncoding(std::string const& data,
244 Encoding encoding)
246 #ifndef CMAKE_BOOTSTRAP
247 std::locale prevLocale =
248 this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
249 this->write(data.data(), data.size());
250 this->imbue(prevLocale);
251 #else
252 static_cast<void>(encoding);
253 this->write(data.data(), data.size());
254 #endif