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"
8 #include "cmStringAlgorithms.h"
9 #include "cmSystemTools.h"
11 #if !defined(CMAKE_BOOTSTRAP)
12 # include <cm3p/zlib.h>
14 # include "cm_codecvt.hxx"
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
)));
24 static_cast<void>(encoding
);
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
)));
43 static_cast<void>(encoding
);
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
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.
71 this->Stream::open( // NOLINT(cmake-use-cmsys-fstream)
72 this->TempName
.c_str(), std::ios::out
| std::ios::binary
);
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("");
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
)
120 cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
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
;
133 this->TempName
+= "_";
135 this->TempName
+= ".";
137 if (!this->TempExt
.empty()) {
138 this->TempName
+= this->TempExt
;
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
) {
162 // Only consider replacing the destination file if no error
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
);
176 this->RenameFile(this->TempName
, resname
);
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
);
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");
200 FILE* ifs
= cmsys::SystemTools::Fopen(oldname
, "r");
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
))) {
220 int cmGeneratedFileStreamBase::CompressFile(std::string
const&,
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
)
243 void cmGeneratedFileStream::WriteAltEncoding(std::string
const& data
,
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
);
252 static_cast<void>(encoding
);
253 this->write(data
.data(), data
.size());