cmGeneratedFileStream.cxx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. #include <stdio.h>
  5. #include "cmSystemTools.h"
  6. #if defined(CMAKE_BUILD_WITH_CMAKE)
  7. #include "cm_codecvt.hxx"
  8. #include "cm_zlib.h"
  9. #endif
  10. cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding)
  11. : cmGeneratedFileStreamBase()
  12. , Stream()
  13. {
  14. #ifdef CMAKE_BUILD_WITH_CMAKE
  15. if (encoding != codecvt::None) {
  16. imbue(std::locale(getloc(), new codecvt(encoding)));
  17. }
  18. #else
  19. static_cast<void>(encoding);
  20. #endif
  21. }
  22. cmGeneratedFileStream::cmGeneratedFileStream(const char* name, bool quiet,
  23. Encoding encoding)
  24. : cmGeneratedFileStreamBase(name)
  25. , Stream(TempName.c_str())
  26. {
  27. // Check if the file opened.
  28. if (!*this && !quiet) {
  29. cmSystemTools::Error("Cannot open file for write: ",
  30. this->TempName.c_str());
  31. cmSystemTools::ReportLastSystemError("");
  32. }
  33. #ifdef CMAKE_BUILD_WITH_CMAKE
  34. if (encoding != codecvt::None) {
  35. imbue(std::locale(getloc(), new codecvt(encoding)));
  36. }
  37. #else
  38. static_cast<void>(encoding);
  39. #endif
  40. }
  41. cmGeneratedFileStream::~cmGeneratedFileStream()
  42. {
  43. // This is the first destructor called. Check the status of the
  44. // stream and give the information to the private base. Next the
  45. // stream will be destroyed which will close the temporary file.
  46. // Finally the base destructor will be called to replace the
  47. // destination file.
  48. this->Okay = !this->fail();
  49. }
  50. cmGeneratedFileStream& cmGeneratedFileStream::Open(const char* name,
  51. bool quiet, bool binaryFlag)
  52. {
  53. // Store the file name and construct the temporary file name.
  54. this->cmGeneratedFileStreamBase::Open(name);
  55. // Open the temporary output file.
  56. if (binaryFlag) {
  57. this->Stream::open(this->TempName.c_str(),
  58. std::ios::out | std::ios::binary);
  59. } else {
  60. this->Stream::open(this->TempName.c_str());
  61. }
  62. // Check if the file opened.
  63. if (!*this && !quiet) {
  64. cmSystemTools::Error("Cannot open file for write: ",
  65. this->TempName.c_str());
  66. cmSystemTools::ReportLastSystemError("");
  67. }
  68. return *this;
  69. }
  70. bool cmGeneratedFileStream::Close()
  71. {
  72. // Save whether the temporary output file is valid before closing.
  73. this->Okay = !this->fail();
  74. // Close the temporary output file.
  75. this->Stream::close();
  76. // Remove the temporary file (possibly by renaming to the real file).
  77. return this->cmGeneratedFileStreamBase::Close();
  78. }
  79. void cmGeneratedFileStream::SetCopyIfDifferent(bool copy_if_different)
  80. {
  81. this->CopyIfDifferent = copy_if_different;
  82. }
  83. void cmGeneratedFileStream::SetCompression(bool compression)
  84. {
  85. this->Compress = compression;
  86. }
  87. void cmGeneratedFileStream::SetCompressionExtraExtension(bool ext)
  88. {
  89. this->CompressExtraExtension = ext;
  90. }
  91. cmGeneratedFileStreamBase::cmGeneratedFileStreamBase()
  92. : Name()
  93. , TempName()
  94. , CopyIfDifferent(false)
  95. , Okay(false)
  96. , Compress(false)
  97. , CompressExtraExtension(true)
  98. {
  99. }
  100. cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(const char* name)
  101. : Name()
  102. , TempName()
  103. , CopyIfDifferent(false)
  104. , Okay(false)
  105. , Compress(false)
  106. , CompressExtraExtension(true)
  107. {
  108. this->Open(name);
  109. }
  110. cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
  111. {
  112. this->Close();
  113. }
  114. void cmGeneratedFileStreamBase::Open(const char* name)
  115. {
  116. // Save the original name of the file.
  117. this->Name = name;
  118. // Create the name of the temporary file.
  119. this->TempName = name;
  120. #if defined(__VMS)
  121. this->TempName += "_tmp";
  122. #else
  123. this->TempName += ".tmp";
  124. #endif
  125. // Make sure the temporary file that will be used is not present.
  126. cmSystemTools::RemoveFile(this->TempName);
  127. std::string dir = cmSystemTools::GetFilenamePath(this->TempName);
  128. cmSystemTools::MakeDirectory(dir);
  129. }
  130. bool cmGeneratedFileStreamBase::Close()
  131. {
  132. bool replaced = false;
  133. std::string resname = this->Name;
  134. if (this->Compress && this->CompressExtraExtension) {
  135. resname += ".gz";
  136. }
  137. // Only consider replacing the destination file if no error
  138. // occurred.
  139. if (!this->Name.empty() && this->Okay &&
  140. (!this->CopyIfDifferent ||
  141. cmSystemTools::FilesDiffer(this->TempName, resname))) {
  142. // The destination is to be replaced. Rename the temporary to the
  143. // destination atomically.
  144. if (this->Compress) {
  145. std::string gzname = this->TempName + ".temp.gz";
  146. if (this->CompressFile(this->TempName.c_str(), gzname.c_str())) {
  147. this->RenameFile(gzname.c_str(), resname.c_str());
  148. }
  149. cmSystemTools::RemoveFile(gzname);
  150. } else {
  151. this->RenameFile(this->TempName.c_str(), resname.c_str());
  152. }
  153. replaced = true;
  154. }
  155. // Else, the destination was not replaced.
  156. //
  157. // Always delete the temporary file. We never want it to stay around.
  158. cmSystemTools::RemoveFile(this->TempName);
  159. return replaced;
  160. }
  161. #ifdef CMAKE_BUILD_WITH_CMAKE
  162. int cmGeneratedFileStreamBase::CompressFile(const char* oldname,
  163. const char* newname)
  164. {
  165. gzFile gf = gzopen(newname, "w");
  166. if (!gf) {
  167. return 0;
  168. }
  169. FILE* ifs = cmsys::SystemTools::Fopen(oldname, "r");
  170. if (!ifs) {
  171. return 0;
  172. }
  173. size_t res;
  174. const size_t BUFFER_SIZE = 1024;
  175. char buffer[BUFFER_SIZE];
  176. while ((res = fread(buffer, 1, BUFFER_SIZE, ifs)) > 0) {
  177. if (!gzwrite(gf, buffer, static_cast<int>(res))) {
  178. fclose(ifs);
  179. gzclose(gf);
  180. return 0;
  181. }
  182. }
  183. fclose(ifs);
  184. gzclose(gf);
  185. return 1;
  186. }
  187. #else
  188. int cmGeneratedFileStreamBase::CompressFile(const char*, const char*)
  189. {
  190. return 0;
  191. }
  192. #endif
  193. int cmGeneratedFileStreamBase::RenameFile(const char* oldname,
  194. const char* newname)
  195. {
  196. return cmSystemTools::RenameFile(oldname, newname);
  197. }
  198. void cmGeneratedFileStream::SetName(const std::string& fname)
  199. {
  200. this->Name = fname;
  201. }