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