1 /*
2  * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
3  *
4  * This file is part of Jam - see jam.c for Copyright information.
5  */
6 
7 /* This file is ALSO:
8  * Copyright 2001-2004 David Abrahams.
9  * Distributed under the Boost Software License, Version 1.0.
10  * (See accompanying file LICENSE_1_0.txt or
11  * http://www.boost.org/LICENSE_1_0.txt)
12  */
13 
14 /*
15  * timestamp.c - get the timestamp of a file or archive member
16  *
17  * External routines:
18  *  timestamp_from_path() - return timestamp for a path, if present
19  *  timestamp_done()      - free timestamp tables
20  *
21  * Internal routines:
22  *  time_enter()      - internal worker callback for scanning archives &
23  *                      directories
24  *  free_timestamps() - worker function for freeing timestamp table contents
25  */
26 
27 #include "jam.h"
28 #include "timestamp.h"
29 
30 #include "filesys.h"
31 #include "hash.h"
32 #include "object.h"
33 #include "pathsys.h"
34 #include "strings.h"
35 #include "output.h"
36 
37 
38 /*
39  * BINDING - all known files
40  */
41 
42 typedef struct _binding
43 {
44     OBJECT * name;
45     short flags;
46 
47 #define BIND_SCANNED  0x01  /* if directory or arch, has been scanned */
48 
49     short progress;
50 
51 #define BIND_INIT     0  /* never seen */
52 #define BIND_NOENTRY  1  /* timestamp requested but file never found */
53 #define BIND_SPOTTED  2  /* file found but not timed yet */
54 #define BIND_MISSING  3  /* file found but can not get timestamp */
55 #define BIND_FOUND    4  /* file found and time stamped */
56 
57     /* update time - cleared if the there is nothing to bind */
58     timestamp time;
59 } BINDING;
60 
61 static struct hash * bindhash = 0;
62 
63 static void time_enter( void *, OBJECT *, int const found,
64     timestamp const * const );
65 
66 static const char * time_progress[] =
67 {
68     "INIT",
69     "NOENTRY",
70     "SPOTTED",
71     "MISSING",
72     "FOUND"
73 };
74 
75 
76 #ifdef OS_NT
77 /*
78  * timestamp_from_filetime() - Windows FILETIME --> timestamp conversion
79  *
80  * Lifted shamelessly from the CPython implementation.
81  */
82 
timestamp_from_filetime(timestamp * const t,FILETIME const * const ft)83 void timestamp_from_filetime( timestamp * const t, FILETIME const * const ft )
84 {
85     /* Seconds between 1.1.1601 and 1.1.1970 */
86     static __int64 const secs_between_epochs = 11644473600;
87 
88     /* We can not simply cast and dereference a FILETIME, since it might not be
89      * aligned properly. __int64 type variables are expected to be aligned to an
90      * 8 byte boundary while FILETIME structures may be aligned to any 4 byte
91      * boundary. Using an incorrectly aligned __int64 variable may cause a
92      * performance penalty on some platforms or even exceptions on others
93      * (documented on MSDN).
94      */
95     __int64 in;
96     memcpy( &in, ft, sizeof( in ) );
97 
98     /* FILETIME resolution: 100ns. */
99     timestamp_init( t, (time_t)( ( in / 10000000 ) - secs_between_epochs ),
100         (int)( in % 10000000 ) * 100 );
101 }
102 #endif  /* OS_NT */
103 
104 
timestamp_clear(timestamp * const time)105 void timestamp_clear( timestamp * const time )
106 {
107     time->secs = time->nsecs = 0;
108 }
109 
110 
timestamp_cmp(timestamp const * const lhs,timestamp const * const rhs)111 int timestamp_cmp( timestamp const * const lhs, timestamp const * const rhs )
112 {
113     return lhs->secs == rhs->secs
114         ? lhs->nsecs - rhs->nsecs
115         : lhs->secs - rhs->secs;
116 }
117 
118 
timestamp_copy(timestamp * const target,timestamp const * const source)119 void timestamp_copy( timestamp * const target, timestamp const * const source )
120 {
121     target->secs = source->secs;
122     target->nsecs = source->nsecs;
123 }
124 
125 
timestamp_current(timestamp * const t)126 void timestamp_current( timestamp * const t )
127 {
128 #ifdef OS_NT
129     /* GetSystemTimeAsFileTime()'s resolution seems to be about 15 ms on Windows
130      * XP and under a millisecond on Windows 7.
131      */
132     FILETIME ft;
133     GetSystemTimeAsFileTime( &ft );
134     timestamp_from_filetime( t, &ft );
135 #elif defined(_POSIX_TIMERS) && defined(CLOCK_REALTIME) && \
136     (!defined(__GLIBC__) || (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))
137     /* Some older versions of XCode define _POSIX_TIMERS, but don't actually
138      * have clock_gettime.  Check CLOCK_REALTIME as well.  Prior to glibc 2.17,
139      * clock_gettime requires -lrt.  This is a non-critical feature, so
140      * we just disable it to keep bootstrapping simple.
141      */
142     struct timespec ts;
143     clock_gettime( CLOCK_REALTIME, &ts );
144     timestamp_init( t, ts.tv_sec, ts.tv_nsec );
145 #else  /* OS_NT */
146     timestamp_init( t, time( 0 ), 0 );
147 #endif  /* OS_NT */
148 }
149 
150 
timestamp_empty(timestamp const * const time)151 int timestamp_empty( timestamp const * const time )
152 {
153     return !time->secs && !time->nsecs;
154 }
155 
156 
157 /*
158  * timestamp_from_path() - return timestamp for a path, if present
159  */
160 
timestamp_from_path(timestamp * const time,OBJECT * const path)161 void timestamp_from_path( timestamp * const time, OBJECT * const path )
162 {
163     PROFILE_ENTER( timestamp );
164 
165     PATHNAME f1;
166     PATHNAME f2;
167     int found;
168     BINDING * b;
169     string buf[ 1 ];
170 
171 
172     if ( file_time( path, time ) < 0 )
173         timestamp_clear( time );
174 
175     PROFILE_EXIT( timestamp );
176 }
177 
178 
timestamp_init(timestamp * const time,time_t const secs,int const nsecs)179 void timestamp_init( timestamp * const time, time_t const secs, int const nsecs
180     )
181 {
182     time->secs = secs;
183     time->nsecs = nsecs;
184 }
185 
186 
timestamp_max(timestamp * const max,timestamp const * const lhs,timestamp const * const rhs)187 void timestamp_max( timestamp * const max, timestamp const * const lhs,
188     timestamp const * const rhs )
189 {
190     if ( timestamp_cmp( lhs, rhs ) > 0 )
191         timestamp_copy( max, lhs );
192     else
193         timestamp_copy( max, rhs );
194 }
195 
196 
timestamp_formatstr(timestamp const * const time,char const * const format)197 static char const * timestamp_formatstr( timestamp const * const time,
198     char const * const format )
199 {
200     static char result1[ 500 ];
201     static char result2[ 500 ];
202     strftime( result1, sizeof( result1 ) / sizeof( *result1 ), format, gmtime(
203         &time->secs ) );
204     sprintf( result2, result1, time->nsecs );
205     return result2;
206 }
207 
208 
timestamp_str(timestamp const * const time)209 char const * timestamp_str( timestamp const * const time )
210 {
211     return timestamp_formatstr( time, "%Y-%m-%d %H:%M:%S.%%09d +0000" );
212 }
213 
214 
timestamp_timestr(timestamp const * const time)215 char const * timestamp_timestr( timestamp const * const time )
216 {
217     return timestamp_formatstr( time, "%H:%M:%S.%%09d" );
218 }
219 
220 
221 /*
222  * time_enter() - internal worker callback for scanning archives & directories
223  */
224 
time_enter(void * closure,OBJECT * target,int const found,timestamp const * const time)225 static void time_enter( void * closure, OBJECT * target, int const found,
226     timestamp const * const time )
227 {
228     int item_found;
229     BINDING * b;
230     struct hash * const bindhash = (struct hash *)closure;
231 
232     target = path_as_key( target );
233 
234     b = (BINDING *)hash_insert( bindhash, target, &item_found );
235     if ( !item_found )
236     {
237         b->name = object_copy( target );
238         b->flags = 0;
239     }
240 
241     timestamp_copy( &b->time, time );
242     b->progress = found ? BIND_FOUND : BIND_SPOTTED;
243 
244     if ( DEBUG_BINDSCAN )
245         out_printf( "time ( %s ) : %s\n", object_str( target ), time_progress[
246             b->progress ] );
247 
248     object_free( target );
249 }
250 
251 
252 /*
253  * free_timestamps() - worker function for freeing timestamp table contents
254  */
255 
free_timestamps(void * xbinding,void * data)256 static void free_timestamps( void * xbinding, void * data )
257 {
258     object_free( ( (BINDING *)xbinding )->name );
259 }
260 
261 
262 /*
263  * timestamp_done() - free timestamp tables
264  */
265 
timestamp_done()266 void timestamp_done()
267 {
268     if ( bindhash )
269     {
270         hashenumerate( bindhash, free_timestamps, 0 );
271         hashdone( bindhash );
272     }
273 }
274 
275 /*
276  * timestamp_delta_seconds() - seconds from time a to b.
277  */
timestamp_delta_seconds(timestamp const * const a,timestamp const * const b)278 double timestamp_delta_seconds( timestamp const * const a , timestamp const * const b )
279 {
280 	return ((b->secs*1000000.0+b->nsecs)-(a->secs*1000000.0+a->nsecs))/1000000.0;
281 }
282