cmGeneratedFileStream.cxx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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. //----------------------------------------------------------------------------
  23. cmGeneratedFileStream::cmGeneratedFileStream(const char* name,
  24. bool copy_if_different,
  25. bool quiet):
  26. cmGeneratedFileStreamBase(name, copy_if_different),
  27. std::ofstream(m_TempName.c_str())
  28. {
  29. // Check if the file opened.
  30. if(!*this && !quiet)
  31. {
  32. cmSystemTools::Error("Cannot open file for write: ", m_TempName.c_str());
  33. cmSystemTools::ReportLastSystemError("");
  34. }
  35. }
  36. //----------------------------------------------------------------------------
  37. cmGeneratedFileStream::~cmGeneratedFileStream()
  38. {
  39. // This is the first destructor called. Check the status of the
  40. // stream and give the information to the private base. Next the
  41. // stream will be destroyed which will close the temporary file.
  42. // Finally the base destructor will be called to replace the
  43. // destination file.
  44. m_Okay = *this;
  45. }
  46. //----------------------------------------------------------------------------
  47. cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(const char* name,
  48. bool copy_if_different):
  49. m_Name(name),
  50. m_TempName(name),
  51. m_CopyIfDifferent(copy_if_different),
  52. m_Okay(false)
  53. {
  54. // Create the name of the temporary file.
  55. m_TempName += ".tmp";
  56. // Make sure the temporary file that will be used is not present.
  57. cmSystemTools::RemoveFile(m_TempName.c_str());
  58. }
  59. //----------------------------------------------------------------------------
  60. cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
  61. {
  62. // Only consider replacing the destination file if no error
  63. // occurred.
  64. if(m_Okay &&
  65. (!m_CopyIfDifferent ||
  66. cmSystemTools::FilesDiffer(m_TempName.c_str(), m_Name.c_str())))
  67. {
  68. // The destination is to be replaced. Rename the temporary to the
  69. // destination atomically.
  70. this->RenameFile(m_TempName.c_str(), m_Name.c_str());
  71. }
  72. else
  73. {
  74. // The destination was not replaced. Just delete the temporary
  75. // file.
  76. cmSystemTools::RemoveFile(m_TempName.c_str());
  77. }
  78. }
  79. //----------------------------------------------------------------------------
  80. int cmGeneratedFileStreamBase::RenameFile(const char* oldname,
  81. const char* newname)
  82. {
  83. #ifdef _WIN32
  84. /* On Windows the move functions will not replace existing files.
  85. Check if the destination exists. */
  86. struct stat newFile;
  87. if(stat(newname, &newFile) == 0)
  88. {
  89. /* The destination exists. We have to replace it carefully. The
  90. MoveFileEx function does what we need but is not available on
  91. Win9x. */
  92. OSVERSIONINFO osv;
  93. DWORD attrs;
  94. /* Make sure the destination is not read only. */
  95. attrs = GetFileAttributes(newname);
  96. if(attrs & FILE_ATTRIBUTE_READONLY)
  97. {
  98. SetFileAttributes(newname, attrs & ~FILE_ATTRIBUTE_READONLY);
  99. }
  100. /* Check the windows version number. */
  101. osv.dwOSVersionInfoSize = sizeof(osv);
  102. GetVersionEx(&osv);
  103. if(osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
  104. {
  105. /* This is Win9x. There is no MoveFileEx implementation. We
  106. cannot quite rename the file atomically. Just delete the
  107. destination and then move the file. */
  108. DeleteFile(newname);
  109. return MoveFile(oldname, newname);
  110. }
  111. else
  112. {
  113. /* This is not Win9x. Use the MoveFileEx implementation. */
  114. return MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING);
  115. }
  116. }
  117. else
  118. {
  119. /* The destination does not exist. Just move the file. */
  120. return MoveFile(oldname, newname);
  121. }
  122. #else
  123. /* On UNIX we have an OS-provided call to do this atomically. */
  124. return rename(oldname, newname) == 0;
  125. #endif
  126. }