You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
8.8 KiB

  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmGeneratedFileStream.h"
  14. #include "cmSystemTools.h"
  15. // Includes needed for implementation of RenameFile. This is not in
  16. // system tools because it is not implemented robustly enough to move
  17. // files across directories.
  18. #ifdef _WIN32
  19. # include <windows.h>
  20. # include <sys/stat.h>
  21. #endif
  22. #if defined(CMAKE_BUILD_WITH_CMAKE)
  23. # include <cm_zlib.h>
  24. #endif
  25. //----------------------------------------------------------------------------
  26. cmGeneratedFileStream::cmGeneratedFileStream():
  27. cmGeneratedFileStreamBase(), Stream()
  28. {
  29. }
  30. //----------------------------------------------------------------------------
  31. cmGeneratedFileStream::cmGeneratedFileStream(const char* name, bool quiet):
  32. cmGeneratedFileStreamBase(name),
  33. Stream(TempName.c_str())
  34. {
  35. // Check if the file opened.
  36. if(!*this && !quiet)
  37. {
  38. cmSystemTools::Error("Cannot open file for write: ",
  39. this->TempName.c_str());
  40. cmSystemTools::ReportLastSystemError("");
  41. }
  42. }
  43. //----------------------------------------------------------------------------
  44. cmGeneratedFileStream::~cmGeneratedFileStream()
  45. {
  46. // This is the first destructor called. Check the status of the
  47. // stream and give the information to the private base. Next the
  48. // stream will be destroyed which will close the temporary file.
  49. // Finally the base destructor will be called to replace the
  50. // destination file.
  51. this->Okay = (*this)?true:false;
  52. }
  53. //----------------------------------------------------------------------------
  54. cmGeneratedFileStream&
  55. cmGeneratedFileStream::Open(const char* name, bool quiet, bool binaryFlag)
  56. {
  57. // Store the file name and construct the temporary file name.
  58. this->cmGeneratedFileStreamBase::Open(name);
  59. // Open the temporary output file.
  60. if ( binaryFlag )
  61. {
  62. this->Stream::open(this->TempName.c_str(),
  63. std::ios::out | std::ios::binary);
  64. }
  65. else
  66. {
  67. this->Stream::open(this->TempName.c_str(), std::ios::out);
  68. }
  69. // Check if the file opened.
  70. if(!*this && !quiet)
  71. {
  72. cmSystemTools::Error("Cannot open file for write: ",
  73. this->TempName.c_str());
  74. cmSystemTools::ReportLastSystemError("");
  75. }
  76. return *this;
  77. }
  78. //----------------------------------------------------------------------------
  79. bool
  80. cmGeneratedFileStream::Close()
  81. {
  82. // Save whether the temporary output file is valid before closing.
  83. this->Okay = (*this)?true:false;
  84. // Close the temporary output file.
  85. this->Stream::close();
  86. // Remove the temporary file (possibly by renaming to the real file).
  87. return this->cmGeneratedFileStreamBase::Close();
  88. }
  89. //----------------------------------------------------------------------------
  90. void cmGeneratedFileStream::SetCopyIfDifferent(bool copy_if_different)
  91. {
  92. this->CopyIfDifferent = copy_if_different;
  93. }
  94. //----------------------------------------------------------------------------
  95. void cmGeneratedFileStream::SetCompression(bool compression)
  96. {
  97. this->Compress = compression;
  98. }
  99. //----------------------------------------------------------------------------
  100. void cmGeneratedFileStream::SetCompressionExtraExtension(bool ext)
  101. {
  102. this->CompressExtraExtension = ext;
  103. }
  104. //----------------------------------------------------------------------------
  105. cmGeneratedFileStreamBase::cmGeneratedFileStreamBase():
  106. Name(),
  107. TempName(),
  108. CopyIfDifferent(false),
  109. Okay(false),
  110. Compress(false),
  111. CompressExtraExtension(true)
  112. {
  113. }
  114. //----------------------------------------------------------------------------
  115. cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(const char* name):
  116. Name(),
  117. TempName(),
  118. CopyIfDifferent(false),
  119. Okay(false),
  120. Compress(false),
  121. CompressExtraExtension(true)
  122. {
  123. this->Open(name);
  124. }
  125. //----------------------------------------------------------------------------
  126. cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
  127. {
  128. this->Close();
  129. }
  130. //----------------------------------------------------------------------------
  131. void cmGeneratedFileStreamBase::Open(const char* name)
  132. {
  133. // Save the original name of the file.
  134. this->Name = name;
  135. // Create the name of the temporary file.
  136. this->TempName = name;
  137. this->TempName += ".tmp";
  138. // Make sure the temporary file that will be used is not present.
  139. cmSystemTools::RemoveFile(this->TempName.c_str());
  140. std::string dir = cmSystemTools::GetFilenamePath(this->TempName);
  141. cmSystemTools::MakeDirectory(dir.c_str());
  142. }
  143. //----------------------------------------------------------------------------
  144. bool cmGeneratedFileStreamBase::Close()
  145. {
  146. bool replaced = false;
  147. std::string resname = this->Name;
  148. if ( this->Compress && this->CompressExtraExtension )
  149. {
  150. resname += ".gz";
  151. }
  152. // Only consider replacing the destination file if no error
  153. // occurred.
  154. if(!this->Name.empty() &&
  155. this->Okay &&
  156. (!this->CopyIfDifferent ||
  157. cmSystemTools::FilesDiffer(this->TempName.c_str(), resname.c_str())))
  158. {
  159. // The destination is to be replaced. Rename the temporary to the
  160. // destination atomically.
  161. if ( this->Compress )
  162. {
  163. std::string gzname = this->TempName + ".temp.gz";
  164. if ( this->CompressFile(this->TempName.c_str(), gzname.c_str()) )
  165. {
  166. this->RenameFile(gzname.c_str(), resname.c_str());
  167. }
  168. cmSystemTools::RemoveFile(gzname.c_str());
  169. }
  170. else
  171. {
  172. this->RenameFile(this->TempName.c_str(), resname.c_str());
  173. }
  174. replaced = true;
  175. }
  176. // Else, the destination was not replaced.
  177. //
  178. // Always delete the temporary file. We never want it to stay around.
  179. cmSystemTools::RemoveFile(this->TempName.c_str());
  180. return replaced;
  181. }
  182. //----------------------------------------------------------------------------
  183. #ifdef CMAKE_BUILD_WITH_CMAKE
  184. int cmGeneratedFileStreamBase::CompressFile(const char* oldname,
  185. const char* newname)
  186. {
  187. gzFile gf = gzopen(newname, "w");
  188. if ( !gf )
  189. {
  190. return 0;
  191. }
  192. FILE* ifs = fopen(oldname, "r");
  193. if ( !ifs )
  194. {
  195. return 0;
  196. }
  197. size_t res;
  198. const size_t BUFFER_SIZE = 1024;
  199. char buffer[BUFFER_SIZE];
  200. while ( (res = fread(buffer, 1, BUFFER_SIZE, ifs)) > 0 )
  201. {
  202. if ( !gzwrite(gf, buffer, static_cast<int>(res)) )
  203. {
  204. fclose(ifs);
  205. gzclose(gf);
  206. return 0;
  207. }
  208. }
  209. fclose(ifs);
  210. gzclose(gf);
  211. return 1;
  212. }
  213. #else
  214. int cmGeneratedFileStreamBase::CompressFile(const char*, const char*)
  215. {
  216. return 0;
  217. }
  218. #endif
  219. //----------------------------------------------------------------------------
  220. int cmGeneratedFileStreamBase::RenameFile(const char* oldname,
  221. const char* newname)
  222. {
  223. #ifdef _WIN32
  224. /* On Windows the move functions will not replace existing files.
  225. Check if the destination exists. */
  226. struct stat newFile;
  227. if(stat(newname, &newFile) == 0)
  228. {
  229. /* The destination exists. We have to replace it carefully. The
  230. MoveFileEx function does what we need but is not available on
  231. Win9x. */
  232. OSVERSIONINFO osv;
  233. DWORD attrs;
  234. /* Make sure the destination is not read only. */
  235. attrs = GetFileAttributes(newname);
  236. if(attrs & FILE_ATTRIBUTE_READONLY)
  237. {
  238. SetFileAttributes(newname, attrs & ~FILE_ATTRIBUTE_READONLY);
  239. }
  240. /* Check the windows version number. */
  241. osv.dwOSVersionInfoSize = sizeof(osv);
  242. GetVersionEx(&osv);
  243. if(osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
  244. {
  245. /* This is Win9x. There is no MoveFileEx implementation. We
  246. cannot quite rename the file atomically. Just delete the
  247. destination and then move the file. */
  248. DeleteFile(newname);
  249. return MoveFile(oldname, newname);
  250. }
  251. else
  252. {
  253. /* This is not Win9x. Use the MoveFileEx implementation. */
  254. return MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING);
  255. }
  256. }
  257. else
  258. {
  259. /* The destination does not exist. Just move the file. */
  260. return MoveFile(oldname, newname);
  261. }
  262. #else
  263. /* On UNIX we have an OS-provided call to do this atomically. */
  264. return rename(oldname, newname) == 0;
  265. #endif
  266. }
  267. //----------------------------------------------------------------------------
  268. void cmGeneratedFileStream::SetName(const char* fname)
  269. {
  270. if ( !fname )
  271. {
  272. this->Name = "";
  273. return;
  274. }
  275. this->Name = fname;
  276. }