/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 | } |