wrap and sort CLEANFILES
[alioth/cvs.git] / windows-NT / JmgStat.c
1 //
2 // Original Authors:  Jonathan M. Gilligan, Tony M. Hoyle
3 //
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the 
7 // Free Software Foundation; either version 2 of the License, or (at your
8 // option) any later version.
9 // 
10 // This program is distributed in the hope that it will be useful, but 
11 // WITHOUT ANY WARRANTY; without even the implied warranty of 
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
13 // General Public License for more details.
14 // 
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc., 
17 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 // 
19 // Modification History:
20 // 18 May 2001, JMG -- First version
21
22 #include <windows.h>
23 #include <tchar.h>
24 #include <time.h>
25 #include <stdbool.h>
26
27 #include "JmgStat.h"
28
29
30 /* Tony Hoyle's function for testing whether a given volume uses UTC or 
31  * local time to record file modification times
32  * 
33  * Reproduced here with permission of Tony Hoyle.
34  * 
35  * This code is copyright by Tony Hoyle and is licensed under the Gnu 
36  * General Public License. (See above)
37  *
38  * NTFS, HPFS, and OWFS store file times as UTC times.
39  * FAT stores file times as local time.
40  *
41  * INPUTS:
42  *      LPCSTR name: fully qualified path
43  *
44  * OUTPUTS:
45  *      Return true if the file system on the volume in question 
46  *      stores file times as UTC
47  */
48 bool IsUTCVolume ( LPCTSTR name )
49 {
50     _TCHAR szDrive[_MAX_DRIVE + 1] = _T("");
51     _TCHAR szFs[32]=_T("");
52     _tsplitpath(name, szDrive, NULL, NULL, NULL);
53
54     _tcscat(szDrive, _T("\\"));
55     GetVolumeInformation( szDrive, NULL, 0, NULL, NULL, NULL, szFs, 32 );
56     return ! ( _tcsicmp( szFs, _T("NTFS") ) 
57                && _tcsicmp( szFs, _T("HPFS") ) 
58                && _tcsicmp( szFs, _T("OWFS") ) );
59 }
60
61 /* Convert a file time to a Unix time_t structure. This function is as 
62  * complicated as it is because it needs to ask what time system the 
63  * filetime describes.
64  * 
65  * INPUTS:
66  *      const FILETIME * ft: A file time. It may be in UTC or in local 
67  *                           time (see local_time, below, for details).
68  *
69  *      time_t * ut:         The destination for the converted time.
70  *
71  *      bool local_time:     TRUE if the time in *ft is in local time 
72  *                           and I need to convert to a real UTC time.
73  *
74  * OUTPUTS:
75  *      time_t * ut:         Store the result in *ut.
76  */
77 static bool FileTimeToUnixTime ( const FILETIME* ft, time_t* ut, bool local_time )
78 {
79     bool success = FALSE;
80     if ( local_time ) 
81     {
82         struct tm atm;
83         SYSTEMTIME st;
84
85         success = FileTimeToSystemTime ( ft, &st );
86
87         /* Important: mktime looks at the tm_isdst field to determine
88          * whether to apply the DST correction. If this field is zero,
89          * then no DST is applied. If the field is one, then DST is
90          * applied. If the field is minus one, then DST is applied
91          * if the United States rule calls for it (DST starts at 
92          * 02:00 on the first Sunday in April and ends at 02:00 on
93          * the last Sunday in October.
94          *
95          * If you are concerned about time zones that follow different 
96          * rules, then you must either use GetTimeZoneInformation() to 
97          * get your system's TIME_ZONE_INFO and use the information
98          * therein to figure out whether the time in question was in 
99          * DST or not, or else use SystemTimeToTzSpecifiedLocalTime()
100          * to do the same.
101          *
102          * I haven't tried playing with SystemTimeToTzSpecifiedLocalTime()
103          * so I am nor sure how well it handles funky stuff.
104          */
105         atm.tm_sec = st.wSecond;
106         atm.tm_min = st.wMinute;
107         atm.tm_hour = st.wHour;
108         atm.tm_mday = st.wDay;
109         /* tm_mon is 0 based */
110         atm.tm_mon = st.wMonth - 1;
111         /* tm_year is 1900 based */
112         atm.tm_year = st.wYear>1900?st.wYear - 1900:st.wYear;     
113         atm.tm_isdst = -1;      /* see notes above */
114         *ut = mktime ( &atm );
115     }
116     else 
117     {
118
119        /* FILETIME = number of 100-nanosecond ticks since midnight 
120         * 1 Jan 1601 UTC. time_t = number of 1-second ticks since 
121         * midnight 1 Jan 1970 UTC. To translate, we subtract a
122         * FILETIME representation of midnight, 1 Jan 1970 from the
123         * time in question and divide by the number of 100-ns ticks
124         * in one second.
125         */
126
127         /* One second = 10,000,000 * 100 nsec */
128         const ULONGLONG second = 10000000L;
129
130         SYSTEMTIME base_st = 
131         {
132             1970,   /* wYear            */
133             1,      /* wMonth           */
134             0,      /* wDayOfWeek       */
135             1,      /* wDay             */
136             0,      /* wHour            */
137             0,      /* wMinute          */
138             0,      /* wSecond          */
139             0       /* wMilliseconds    */
140         };
141         
142         ULARGE_INTEGER itime;
143         FILETIME base_ft;
144
145         success = SystemTimeToFileTime ( &base_st, &base_ft );
146         if (success) 
147         {
148             itime.QuadPart = ((ULARGE_INTEGER *)ft)->QuadPart;
149
150             itime.QuadPart -= ((ULARGE_INTEGER *)&base_ft)->QuadPart;
151             itime.QuadPart /= second;
152
153             *ut = itime.LowPart;
154         }
155     }
156     if (!success)
157     {
158         *ut = -1;   /* error value used by mktime() */
159     }
160     return success;
161 }
162
163 /* Get file modification time using FileTimeToUnixTime()
164  *
165  * INPUTS:
166  *      LPCTSTR name:   the file name
167  */
168 bool GetUTCFileModTime ( LPCTSTR name, time_t * utc_mod_time )
169 {
170     WIN32_FIND_DATA find_buf;
171     FILETIME mod_time;
172     HANDLE find_handle;
173     bool success = FALSE;
174
175     * utc_mod_time = 0L;
176
177     find_handle = FindFirstFile ( name, &find_buf );
178     success = ( find_handle != INVALID_HANDLE_VALUE );
179     if (success)
180     {
181         /* Originally I thought that I needed to apply a correction 
182          * LocalTimeToFileTime() to files from FAT volumes, but the 
183          * FindFirstFile() system call thoughtfully applies this 
184          * correction itself.
185          *
186          * Thus, the file time returned is allegedly in UTC.
187          *
188          * However, the correction from local to UTC is applied 
189          * incorrectly (Thanks a lot, Microsoft!). As documented in the 
190          * Win32 API (see MSDN or the PSDK), DST is applied if and only 
191          * if the computer's system time is in DST at the time we call 
192          * FindFirstFile(), irrespective or whether DST applied at the 
193          * time the file was modified!
194          *
195          * Thus, we have to call FileTimeToLocalFileTime() to undo
196          * Windows's good intentions. We correctly translate the time
197          * In FileTimeToUnixTime().
198          *
199          */
200         if ( IsUTCVolume ( name ) ) 
201         {
202             mod_time = find_buf.ftLastWriteTime;
203             success = FileTimeToUnixTime ( &mod_time, utc_mod_time, FALSE );
204         }
205         else 
206         { 
207             // See notes above...
208             success = FileTimeToLocalFileTime ( &find_buf.ftLastWriteTime, &mod_time );
209             success = success && FileTimeToUnixTime ( &mod_time, utc_mod_time, TRUE );
210         }        
211     }
212     FindClose ( find_handle );
213     return success;
214 }