text: /*
   * 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"
  #include "output.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 )
          out_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 );
      }
  }
  