| /* |
| * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. |
| * |
| * This file is part of Jam - see jam.c for Copyright information. |
| */ |
| |
| /* This file is ALSO: |
| * Copyright 2001-2004 David Abrahams. |
| * Distributed under the Boost Software License, Version 1.0. |
| * (See accompanying file LICENSE_1_0.txt or |
| * http://www.boost.org/LICENSE_1_0.txt) |
| */ |
| |
| /* |
| * timestamp.c - get the timestamp of a file or archive member |
| * |
| * External routines: |
| * timestamp_from_path() - return timestamp for a path, if present |
| * timestamp_done() - free timestamp tables |
| * |
| * Internal routines: |
| * time_enter() - internal worker callback for scanning archives & |
| * directories |
| * free_timestamps() - worker function for freeing timestamp table contents |
| */ |
| |
| #include "jam.h" |
| #include "timestamp.h" |
| |
| #include "filesys.h" |
| #include "hash.h" |
| #include "object.h" |
| #include "pathsys.h" |
| #include "strings.h" |
| |
| |
| /* |
| * BINDING - all known files |
| */ |
| |
| typedef struct _binding |
| { |
| OBJECT * name; |
| short flags; |
| |
| #define BIND_SCANNED 0x01 /* if directory or arch, has been scanned */ |
| |
| short progress; |
| |
| #define BIND_INIT 0 /* never seen */ |
| #define BIND_NOENTRY 1 /* timestamp requested but file never found */ |
| #define BIND_SPOTTED 2 /* file found but not timed yet */ |
| #define BIND_MISSING 3 /* file found but can not get timestamp */ |
| #define BIND_FOUND 4 /* file found and time stamped */ |
| |
| /* update time - cleared if the there is nothing to bind */ |
| timestamp time; |
| } BINDING; |
| |
| static struct hash * bindhash = 0; |
| |
| static void time_enter( void *, OBJECT *, int const found, |
| timestamp const * const ); |
| |
| static char * time_progress[] = |
| { |
| "INIT", |
| "NOENTRY", |
| "SPOTTED", |
| "MISSING", |
| "FOUND" |
| }; |
| |
| |
| #ifdef OS_NT |
| /* |
| * timestamp_from_filetime() - Windows FILETIME --> timestamp conversion |
| * |
| * Lifted shamelessly from the CPython implementation. |
| */ |
| |
| void timestamp_from_filetime( timestamp * const t, FILETIME const * const ft ) |
| { |
| /* Seconds between 1.1.1601 and 1.1.1970 */ |
| static __int64 const secs_between_epochs = 11644473600; |
| |
| /* We can not simply cast and dereference a FILETIME, since it might not be |
| * aligned properly. __int64 type variables are expected to be aligned to an |
| * 8 byte boundary while FILETIME structures may be aligned to any 4 byte |
| * boundary. Using an incorrectly aligned __int64 variable may cause a |
| * performance penalty on some platforms or even exceptions on others |
| * (documented on MSDN). |
| */ |
| __int64 in; |
| memcpy( &in, ft, sizeof( in ) ); |
| |
| /* FILETIME resolution: 100ns. */ |
| timestamp_init( t, (time_t)( ( in / 10000000 ) - secs_between_epochs ), |
| (int)( in % 10000000 ) * 100 ); |
| } |
| #endif /* OS_NT */ |
| |
| |
| void timestamp_clear( timestamp * const time ) |
| { |
| time->secs = time->nsecs = 0; |
| } |
| |
| |
| int timestamp_cmp( timestamp const * const lhs, timestamp const * const rhs ) |
| { |
| return lhs->secs == rhs->secs |
| ? lhs->nsecs - rhs->nsecs |
| : lhs->secs - rhs->secs; |
| } |
| |
| |
| void timestamp_copy( timestamp * const target, timestamp const * const source ) |
| { |
| target->secs = source->secs; |
| target->nsecs = source->nsecs; |
| } |
| |
| |
| void timestamp_current( timestamp * const t ) |
| { |
| #ifdef OS_NT |
| /* GetSystemTimeAsFileTime()'s resolution seems to be about 15 ms on Windows |
| * XP and under a millisecond on Windows 7. |
| */ |
| FILETIME ft; |
| GetSystemTimeAsFileTime( &ft ); |
| timestamp_from_filetime( t, &ft ); |
| #else /* OS_NT */ |
| timestamp_init( t, time( 0 ), 0 ); |
| #endif /* OS_NT */ |
| } |
| |
| |
| int timestamp_empty( timestamp const * const time ) |
| { |
| return !time->secs && !time->nsecs; |
| } |
| |
| |
| /* |
| * timestamp_from_path() - return timestamp for a path, if present |
| */ |
| |
| void timestamp_from_path( timestamp * const time, OBJECT * const path ) |
| { |
| PROFILE_ENTER( timestamp ); |
| |
| PATHNAME f1; |
| PATHNAME f2; |
| int found; |
| BINDING * b; |
| string buf[ 1 ]; |
| |
| |
| if ( file_time( path, time ) < 0 ) |
| timestamp_clear( time ); |
| |
| PROFILE_EXIT( timestamp ); |
| } |
| |
| |
| void timestamp_init( timestamp * const time, time_t const secs, int const nsecs |
| ) |
| { |
| time->secs = secs; |
| time->nsecs = nsecs; |
| } |
| |
| |
| void timestamp_max( timestamp * const max, timestamp const * const lhs, |
| timestamp const * const rhs ) |
| { |
| if ( timestamp_cmp( lhs, rhs ) > 0 ) |
| timestamp_copy( max, lhs ); |
| else |
| timestamp_copy( max, rhs ); |
| } |
| |
| |
| static char const * timestamp_formatstr( timestamp const * const time, |
| char const * const format ) |
| { |
| static char result1[ 500 ]; |
| static char result2[ 500 ]; |
| strftime( result1, sizeof( result1 ) / sizeof( *result1 ), format, gmtime( |
| &time->secs ) ); |
| sprintf( result2, result1, time->nsecs ); |
| return result2; |
| } |
| |
| |
| char const * timestamp_str( timestamp const * const time ) |
| { |
| return timestamp_formatstr( time, "%Y-%m-%d %H:%M:%S.%%09d +0000" ); |
| } |
| |
| |
| char const * timestamp_timestr( timestamp const * const time ) |
| { |
| return timestamp_formatstr( time, "%H:%M:%S.%%09d" ); |
| } |
| |
| |
| /* |
| * time_enter() - internal worker callback for scanning archives & directories |
| */ |
| |
| static void time_enter( void * closure, OBJECT * target, int const found, |
| timestamp const * const time ) |
| { |
| int item_found; |
| BINDING * b; |
| struct hash * const bindhash = (struct hash *)closure; |
| |
| target = path_as_key( target ); |
| |
| b = (BINDING *)hash_insert( bindhash, target, &item_found ); |
| if ( !item_found ) |
| { |
| b->name = object_copy( target ); |
| b->flags = 0; |
| } |
| |
| timestamp_copy( &b->time, time ); |
| b->progress = found ? BIND_FOUND : BIND_SPOTTED; |
| |
| if ( DEBUG_BINDSCAN ) |
| printf( "time ( %s ) : %s\n", object_str( target ), time_progress[ |
| b->progress ] ); |
| |
| object_free( target ); |
| } |
| |
| |
| /* |
| * free_timestamps() - worker function for freeing timestamp table contents |
| */ |
| |
| static void free_timestamps( void * xbinding, void * data ) |
| { |
| object_free( ( (BINDING *)xbinding )->name ); |
| } |
| |
| |
| /* |
| * timestamp_done() - free timestamp tables |
| */ |
| |
| void timestamp_done() |
| { |
| if ( bindhash ) |
| { |
| hashenumerate( bindhash, free_timestamps, 0 ); |
| hashdone( bindhash ); |
| } |
| } |