1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmGeneratedFileStream.cxx,v $
6 Date: $Date: 2007/11/16 12:01:58 $
7 Version: $Revision: 1.19 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmGeneratedFileStream.h"
19 #include "cmSystemTools.h"
21 // Includes needed for implementation of RenameFile. This is not in
22 // system tools because it is not implemented robustly enough to move
23 // files across directories.
26 # include <sys/stat.h>
29 #if defined(CMAKE_BUILD_WITH_CMAKE)
33 //----------------------------------------------------------------------------
34 cmGeneratedFileStream::cmGeneratedFileStream():
35 cmGeneratedFileStreamBase(), Stream()
39 //----------------------------------------------------------------------------
40 cmGeneratedFileStream::cmGeneratedFileStream(const char* name
, bool quiet
):
41 cmGeneratedFileStreamBase(name
),
42 Stream(TempName
.c_str())
44 // Check if the file opened.
47 cmSystemTools::Error("Cannot open file for write: ",
48 this->TempName
.c_str());
49 cmSystemTools::ReportLastSystemError("");
53 //----------------------------------------------------------------------------
54 cmGeneratedFileStream::~cmGeneratedFileStream()
56 // This is the first destructor called. Check the status of the
57 // stream and give the information to the private base. Next the
58 // stream will be destroyed which will close the temporary file.
59 // Finally the base destructor will be called to replace the
61 this->Okay
= (*this)?true:false;
64 //----------------------------------------------------------------------------
65 cmGeneratedFileStream
&
66 cmGeneratedFileStream::Open(const char* name
, bool quiet
, bool binaryFlag
)
68 // Store the file name and construct the temporary file name.
69 this->cmGeneratedFileStreamBase::Open(name
);
71 // Open the temporary output file.
74 this->Stream::open(this->TempName
.c_str(),
75 std::ios::out
| std::ios::binary
);
79 this->Stream::open(this->TempName
.c_str(), std::ios::out
);
82 // Check if the file opened.
85 cmSystemTools::Error("Cannot open file for write: ",
86 this->TempName
.c_str());
87 cmSystemTools::ReportLastSystemError("");
92 //----------------------------------------------------------------------------
94 cmGeneratedFileStream::Close()
96 // Save whether the temporary output file is valid before closing.
97 this->Okay
= (*this)?true:false;
99 // Close the temporary output file.
100 this->Stream::close();
102 // Remove the temporary file (possibly by renaming to the real file).
103 return this->cmGeneratedFileStreamBase::Close();
106 //----------------------------------------------------------------------------
107 void cmGeneratedFileStream::SetCopyIfDifferent(bool copy_if_different
)
109 this->CopyIfDifferent
= copy_if_different
;
112 //----------------------------------------------------------------------------
113 void cmGeneratedFileStream::SetCompression(bool compression
)
115 this->Compress
= compression
;
118 //----------------------------------------------------------------------------
119 void cmGeneratedFileStream::SetCompressionExtraExtension(bool ext
)
121 this->CompressExtraExtension
= ext
;
124 //----------------------------------------------------------------------------
125 cmGeneratedFileStreamBase::cmGeneratedFileStreamBase():
128 CopyIfDifferent(false),
131 CompressExtraExtension(true)
135 //----------------------------------------------------------------------------
136 cmGeneratedFileStreamBase::cmGeneratedFileStreamBase(const char* name
):
139 CopyIfDifferent(false),
142 CompressExtraExtension(true)
147 //----------------------------------------------------------------------------
148 cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
153 //----------------------------------------------------------------------------
154 void cmGeneratedFileStreamBase::Open(const char* name
)
156 // Save the original name of the file.
159 // Create the name of the temporary file.
160 this->TempName
= name
;
161 this->TempName
+= ".tmp";
163 // Make sure the temporary file that will be used is not present.
164 cmSystemTools::RemoveFile(this->TempName
.c_str());
166 std::string dir
= cmSystemTools::GetFilenamePath(this->TempName
);
167 cmSystemTools::MakeDirectory(dir
.c_str());
170 //----------------------------------------------------------------------------
171 bool cmGeneratedFileStreamBase::Close()
173 bool replaced
= false;
175 std::string resname
= this->Name
;
176 if ( this->Compress
&& this->CompressExtraExtension
)
181 // Only consider replacing the destination file if no error
183 if(!this->Name
.empty() &&
185 (!this->CopyIfDifferent
||
186 cmSystemTools::FilesDiffer(this->TempName
.c_str(), resname
.c_str())))
188 // The destination is to be replaced. Rename the temporary to the
189 // destination atomically.
190 if ( this->Compress
)
192 std::string gzname
= this->TempName
+ ".temp.gz";
193 if ( this->CompressFile(this->TempName
.c_str(), gzname
.c_str()) )
195 this->RenameFile(gzname
.c_str(), resname
.c_str());
197 cmSystemTools::RemoveFile(gzname
.c_str());
201 this->RenameFile(this->TempName
.c_str(), resname
.c_str());
207 // Else, the destination was not replaced.
209 // Always delete the temporary file. We never want it to stay around.
210 cmSystemTools::RemoveFile(this->TempName
.c_str());
215 //----------------------------------------------------------------------------
216 #ifdef CMAKE_BUILD_WITH_CMAKE
217 int cmGeneratedFileStreamBase::CompressFile(const char* oldname
,
220 gzFile gf
= gzopen(newname
, "w");
225 FILE* ifs
= fopen(oldname
, "r");
231 const size_t BUFFER_SIZE
= 1024;
232 char buffer
[BUFFER_SIZE
];
233 while ( (res
= fread(buffer
, 1, BUFFER_SIZE
, ifs
)) > 0 )
235 if ( !gzwrite(gf
, buffer
, static_cast<int>(res
)) )
247 int cmGeneratedFileStreamBase::CompressFile(const char*, const char*)
253 //----------------------------------------------------------------------------
254 int cmGeneratedFileStreamBase::RenameFile(const char* oldname
,
258 /* On Windows the move functions will not replace existing files.
259 Check if the destination exists. */
261 if(stat(newname
, &newFile
) == 0)
263 /* The destination exists. We have to replace it carefully. The
264 MoveFileEx function does what we need but is not available on
269 /* Make sure the destination is not read only. */
270 attrs
= GetFileAttributes(newname
);
271 if(attrs
& FILE_ATTRIBUTE_READONLY
)
273 SetFileAttributes(newname
, attrs
& ~FILE_ATTRIBUTE_READONLY
);
276 /* Check the windows version number. */
277 osv
.dwOSVersionInfoSize
= sizeof(osv
);
279 if(osv
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
)
281 /* This is Win9x. There is no MoveFileEx implementation. We
282 cannot quite rename the file atomically. Just delete the
283 destination and then move the file. */
285 return MoveFile(oldname
, newname
);
289 /* This is not Win9x. Use the MoveFileEx implementation. */
290 return MoveFileEx(oldname
, newname
, MOVEFILE_REPLACE_EXISTING
);
295 /* The destination does not exist. Just move the file. */
296 return MoveFile(oldname
, newname
);
299 /* On UNIX we have an OS-provided call to do this atomically. */
300 return rename(oldname
, newname
) == 0;
304 //----------------------------------------------------------------------------
305 void cmGeneratedFileStream::SetName(const char* fname
)