Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Source / cmGeneratedFileStream.cxx
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"
4
5 #include <cstdio>
6
7 #include "cmStringAlgorithms.h"
8 #include "cmSystemTools.h"
9
10 #if !defined(CMAKE_BOOTSTRAP)
11 #  include <cm3p/zlib.h>
12
13 #  include "cm_codecvt.hxx"
14 #endif
15
16 cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding)
17 {
18 #ifndef CMAKE_BOOTSTRAP
19   if (encoding != codecvt::None) {
20     this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
21   }
22 #else
23   static_cast<void>(encoding);
24 #endif
25 }
26
27 cmGeneratedFileStream::cmGeneratedFileStream(std::string const& name,
28                                              bool quiet, Encoding encoding)
29   : cmGeneratedFileStreamBase(name)
30   , Stream(this->TempName.c_str())
31 {
32   // Check if the file opened.
33   if (!*this && !quiet) {
34     cmSystemTools::Error("Cannot open file for write: " + this->TempName);
35     cmSystemTools::ReportLastSystemError("");
36   }
37 #ifndef CMAKE_BOOTSTRAP
38   if (encoding != codecvt::None) {
39     this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
40   }
41 #else
42   static_cast<void>(encoding);
43 #endif
44   if (encoding == codecvt::UTF8_WITH_BOM) {
45     // Write the BOM encoding header into the file
46     char magic[] = { static_cast<char>(0xEF), static_cast<char>(0xBB),
47                      static_cast<char>(0xBF) };
48     this->write(magic, 3);
49   }
50 }
51
52 cmGeneratedFileStream::~cmGeneratedFileStream()
53 {
54   // This is the first destructor called.  Check the status of the
55   // stream and give the information to the private base.  Next the
56   // stream will be destroyed which will close the temporary file.
57   // Finally the base destructor will be called to replace the
58   // destination file.
59   this->Okay = !this->fail();
60 }
61
62 cmGeneratedFileStream& cmGeneratedFileStream::Open(std::string const& name,
63                                                    bool quiet, bool binaryFlag)
64 {
65   // Store the file name and construct the temporary file name.
66   this->cmGeneratedFileStreamBase::Open(name);
67
68   // Open the temporary output file.
69   if (binaryFlag) {
70     this->Stream::open(this->TempName.c_str(),
71                        std::ios::out | std::ios::binary);
72   } else {
73     this->Stream::open(this->TempName.c_str());
74   }
75
76   // Check if the file opened.
77   if (!*this && !quiet) {
78     cmSystemTools::Error("Cannot open file for write: " + this->TempName);
79     cmSystemTools::ReportLastSystemError("");
80   }
81   return *this;
82 }
83
84 bool cmGeneratedFileStream::Close()
85 {
86   // Save whether the temporary output file is valid before closing.
87   this->Okay = !this->fail();
88
89   // Close the temporary output file.
90   this->Stream::close();
91
92   // Remove the temporary file (possibly by renaming to the real file).
93   return this->cmGeneratedFileStreamBase::Close();
94 }
95
96 void cmGeneratedFileStream::SetCopyIfDifferent(bool copy_if_different)
97 {
98   this->CopyIfDifferent = copy_if_different;
99 }
100
101 void cmGeneratedFileStream::SetCompression(bool compression)
102 {
103   this->Compress = compression;
104 }
105
106 void cmGeneratedFileStream::SetCompressionExtraExtension(bool ext)
107 {
108   this->CompressExtraExtension = ext;
109 }
110
111 cmGeneratedFileStreamBase::cmGeneratedFileStreamBase() = default;
112
113 cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(std::string const& name)
114 {
115   this->Open(name);
116 }
117
118 cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
119 {
120   this->Close();
121 }
122
123 void cmGeneratedFileStreamBase::Open(std::string const& name)
124 {
125   // Save the original name of the file.
126   this->Name = cmSystemTools::CollapseFullPath(name);
127
128   // Create the name of the temporary file.
129   this->TempName = this->Name;
130 #if defined(__VMS)
131   this->TempName += "_";
132 #else
133   this->TempName += ".";
134 #endif
135   if (!this->TempExt.empty()) {
136     this->TempName += this->TempExt;
137   } else {
138     char buf[64];
139     snprintf(buf, sizeof(buf), "tmp%05x",
140              cmSystemTools::RandomSeed() & 0xFFFFF);
141     this->TempName += buf;
142   }
143
144   // Make sure the temporary file that will be used is not present.
145   cmSystemTools::RemoveFile(this->TempName);
146
147   std::string dir = cmSystemTools::GetFilenamePath(this->TempName);
148   cmSystemTools::MakeDirectory(dir);
149 }
150
151 bool cmGeneratedFileStreamBase::Close()
152 {
153   bool replaced = false;
154
155   std::string resname = this->Name;
156   if (this->Compress && this->CompressExtraExtension) {
157     resname += ".gz";
158   }
159
160   // Only consider replacing the destination file if no error
161   // occurred.
162   if (!this->Name.empty() && this->Okay &&
163       (!this->CopyIfDifferent ||
164        cmSystemTools::FilesDiffer(this->TempName, resname))) {
165     // The destination is to be replaced.  Rename the temporary to the
166     // destination atomically.
167     if (this->Compress) {
168       std::string gzname = cmStrCat(this->TempName, ".temp.gz");
169       if (this->CompressFile(this->TempName, gzname)) {
170         this->RenameFile(gzname, resname);
171       }
172       cmSystemTools::RemoveFile(gzname);
173     } else {
174       this->RenameFile(this->TempName, resname);
175     }
176
177     replaced = true;
178   }
179
180   // Else, the destination was not replaced.
181   //
182   // Always delete the temporary file. We never want it to stay around.
183   if (!this->TempName.empty()) {
184     cmSystemTools::RemoveFile(this->TempName);
185   }
186
187   return replaced;
188 }
189
190 #ifndef CMAKE_BOOTSTRAP
191 int cmGeneratedFileStreamBase::CompressFile(std::string const& oldname,
192                                             std::string const& newname)
193 {
194   gzFile gf = gzopen(newname.c_str(), "w");
195   if (!gf) {
196     return 0;
197   }
198   FILE* ifs = cmsys::SystemTools::Fopen(oldname, "r");
199   if (!ifs) {
200     gzclose(gf);
201     return 0;
202   }
203   size_t res;
204   const size_t BUFFER_SIZE = 1024;
205   char buffer[BUFFER_SIZE];
206   while ((res = fread(buffer, 1, BUFFER_SIZE, ifs)) > 0) {
207     if (!gzwrite(gf, buffer, static_cast<int>(res))) {
208       fclose(ifs);
209       gzclose(gf);
210       return 0;
211     }
212   }
213   fclose(ifs);
214   gzclose(gf);
215   return 1;
216 }
217 #else
218 int cmGeneratedFileStreamBase::CompressFile(std::string const&,
219                                             std::string const&)
220 {
221   return 0;
222 }
223 #endif
224
225 int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname,
226                                           std::string const& newname)
227 {
228   return cmSystemTools::RenameFile(oldname, newname);
229 }
230
231 void cmGeneratedFileStream::SetName(const std::string& fname)
232 {
233   this->Name = cmSystemTools::CollapseFullPath(fname);
234 }
235
236 void cmGeneratedFileStream::SetTempExt(std::string const& ext)
237 {
238   this->TempExt = ext;
239 }
240
241 void cmGeneratedFileStream::WriteAltEncoding(std::string const& data,
242                                              Encoding encoding)
243 {
244 #ifndef CMAKE_BOOTSTRAP
245   std::locale prevLocale =
246     this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
247   this->write(data.data(), data.size());
248   this->imbue(prevLocale);
249 #else
250   static_cast<void>(encoding);
251   this->write(data.data(), data.size());
252 #endif
253 }