Imported Upstream version 2.8.12.2
[platform/upstream/cmake.git] / Source / cmFileTimeComparison.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmFileTimeComparison.h"
13
14 // Use a hash table to avoid duplicate file time checks from disk.
15 #if defined(CMAKE_BUILD_WITH_CMAKE)
16 # include <cmsys/hash_map.hxx>
17 #endif
18
19 // Use a platform-specific API to get file times efficiently.
20 #if !defined(_WIN32) || defined(__CYGWIN__)
21 #  define cmFileTimeComparison_Type struct stat
22 #  include <ctype.h>
23 #  include <sys/stat.h>
24 #else
25 #  define cmFileTimeComparison_Type FILETIME
26 #  include <windows.h>
27 #endif
28
29 //----------------------------------------------------------------------------
30 class cmFileTimeComparisonInternal
31 {
32 public:
33   // Internal comparison method.
34   inline bool FileTimeCompare(const char* f1, const char* f2, int* result);
35
36   bool FileTimesDiffer(const char* f1, const char* f2);
37
38 private:
39 #if defined(CMAKE_BUILD_WITH_CMAKE)
40   // Use a hash table to efficiently map from file name to modification time.
41   class HashString
42     {
43   public:
44     size_t operator()(const cmStdString& s) const
45       {
46       return h(s.c_str());
47       }
48     cmsys::hash<const char*> h;
49     };
50   typedef cmsys::hash_map<cmStdString,
51                           cmFileTimeComparison_Type, HashString> FileStatsMap;
52   FileStatsMap Files;
53 #endif
54
55   // Internal methods to lookup and compare modification times.
56   inline bool Stat(const char* fname, cmFileTimeComparison_Type* st);
57   inline int Compare(cmFileTimeComparison_Type* st1,
58                      cmFileTimeComparison_Type* st2);
59   inline bool TimesDiffer(cmFileTimeComparison_Type* st1,
60                           cmFileTimeComparison_Type* st2);
61 };
62
63 //----------------------------------------------------------------------------
64 bool cmFileTimeComparisonInternal::Stat(const char* fname,
65                                         cmFileTimeComparison_Type* st)
66 {
67 #if defined(CMAKE_BUILD_WITH_CMAKE)
68   // Use the stored time if available.
69   cmFileTimeComparisonInternal::FileStatsMap::iterator fit =
70     this->Files.find(fname);
71   if ( fit != this->Files.end() )
72     {
73     *st = fit->second;
74     return true;
75     }
76 #endif
77
78 #if !defined(_WIN32) || defined(__CYGWIN__)
79   // POSIX version.  Use the stat function.
80   int res = ::stat(fname, st);
81   if ( res != 0 )
82     {
83     return false;
84     }
85 #else
86   // Windows version.  Get the modification time from extended file
87   // attributes.
88   WIN32_FILE_ATTRIBUTE_DATA fdata;
89   if(!GetFileAttributesEx(fname, GetFileExInfoStandard, &fdata))
90     {
91     return false;
92     }
93
94   // Copy the file time to the output location.
95   *st = fdata.ftLastWriteTime;
96 #endif
97
98 #if defined(CMAKE_BUILD_WITH_CMAKE)
99   // Store the time for future use.
100   this->Files[fname] = *st;
101 #endif
102
103   return true;
104 }
105
106 //----------------------------------------------------------------------------
107 cmFileTimeComparison::cmFileTimeComparison()
108 {
109   this->Internals = new cmFileTimeComparisonInternal;
110 }
111
112 //----------------------------------------------------------------------------
113 cmFileTimeComparison::~cmFileTimeComparison()
114 {
115   delete this->Internals;
116 }
117
118 //----------------------------------------------------------------------------
119 bool cmFileTimeComparison::FileTimeCompare(const char* f1,
120                                            const char* f2, int* result)
121 {
122   return this->Internals->FileTimeCompare(f1, f2, result);
123 }
124
125 //----------------------------------------------------------------------------
126 bool cmFileTimeComparison::FileTimesDiffer(const char* f1, const char* f2)
127 {
128   return this->Internals->FileTimesDiffer(f1, f2);
129 }
130
131 //----------------------------------------------------------------------------
132 int cmFileTimeComparisonInternal::Compare(cmFileTimeComparison_Type* s1,
133                                           cmFileTimeComparison_Type* s2)
134 {
135 #if !defined(_WIN32) || defined(__CYGWIN__)
136 # if cmsys_STAT_HAS_ST_MTIM
137   // Compare using nanosecond resolution.
138   if(s1->st_mtim.tv_sec < s2->st_mtim.tv_sec)
139     {
140     return -1;
141     }
142   else if(s1->st_mtim.tv_sec > s2->st_mtim.tv_sec)
143     {
144     return 1;
145     }
146   else if(s1->st_mtim.tv_nsec < s2->st_mtim.tv_nsec)
147     {
148     return -1;
149     }
150   else if(s1->st_mtim.tv_nsec > s2->st_mtim.tv_nsec)
151     {
152     return 1;
153     }
154 # else
155   // Compare using 1 second resolution.
156   if(s1->st_mtime < s2->st_mtime)
157     {
158     return -1;
159     }
160   else if(s1->st_mtime > s2->st_mtime)
161     {
162     return 1;
163     }
164 # endif
165   // Files have the same time.
166   return 0;
167 #else
168   // Compare using system-provided function.
169   return (int)CompareFileTime(s1, s2);
170 #endif
171 }
172
173 //----------------------------------------------------------------------------
174 bool cmFileTimeComparisonInternal::TimesDiffer(cmFileTimeComparison_Type* s1,
175                                                cmFileTimeComparison_Type* s2)
176 {
177 #if !defined(_WIN32) || defined(__CYGWIN__)
178 # if cmsys_STAT_HAS_ST_MTIM
179   // Times are integers in units of 1ns.
180   long long bil = 1000000000;
181   long long t1 = s1->st_mtim.tv_sec * bil + s1->st_mtim.tv_nsec;
182   long long t2 = s2->st_mtim.tv_sec * bil + s2->st_mtim.tv_nsec;
183   if(t1 < t2)
184     {
185     return (t2 - t1) >= bil;
186     }
187   else if(t2 < t1)
188     {
189     return (t1 - t2) >= bil;
190     }
191   else
192     {
193     return false;
194     }
195 # else
196   // Times are integers in units of 1s.
197   if(s1->st_mtime < s2->st_mtime)
198     {
199     return (s2->st_mtime - s1->st_mtime) >= 1;
200     }
201   else if(s1->st_mtime > s2->st_mtime)
202     {
203     return (s1->st_mtime - s2->st_mtime) >= 1;
204     }
205   else
206     {
207     return false;
208     }
209 # endif
210 #else
211   // Times are integers in units of 100ns.
212   LARGE_INTEGER t1;
213   LARGE_INTEGER t2;
214   t1.LowPart = s1->dwLowDateTime;
215   t1.HighPart = s1->dwHighDateTime;
216   t2.LowPart = s2->dwLowDateTime;
217   t2.HighPart = s2->dwHighDateTime;
218   if(t1.QuadPart < t2.QuadPart)
219     {
220     return (t2.QuadPart - t1.QuadPart) >= static_cast<LONGLONG>(10000000);
221     }
222   else if(t2.QuadPart < t1.QuadPart)
223     {
224     return (t1.QuadPart - t2.QuadPart) >= static_cast<LONGLONG>(10000000);
225     }
226   else
227     {
228     return false;
229     }
230 #endif
231 }
232
233 //----------------------------------------------------------------------------
234 bool cmFileTimeComparisonInternal::FileTimeCompare(const char* f1,
235                                                    const char* f2,
236                                                    int* result)
237 {
238   // Get the modification time for each file.
239   cmFileTimeComparison_Type s1;
240   cmFileTimeComparison_Type s2;
241   if(this->Stat(f1, &s1) &&
242      this->Stat(f2, &s2))
243     {
244     // Compare the two modification times.
245     *result = this->Compare(&s1, &s2);
246     return true;
247     }
248   else
249     {
250     // No comparison available.  Default to the same time.
251     *result = 0;
252     return false;
253     }
254 }
255
256 //----------------------------------------------------------------------------
257 bool cmFileTimeComparisonInternal::FileTimesDiffer(const char* f1,
258                                                    const char* f2)
259 {
260   // Get the modification time for each file.
261   cmFileTimeComparison_Type s1;
262   cmFileTimeComparison_Type s2;
263   if(this->Stat(f1, &s1) &&
264      this->Stat(f2, &s2))
265     {
266     // Compare the two modification times.
267     return this->TimesDiffer(&s1, &s2);
268     }
269   else
270     {
271     // No comparison available.  Default to different times.
272     return true;
273     }
274 }