summaryrefslogtreecommitdiff
path: root/tools/build/src/engine/timestamp.c
blob: 0d016985e0bb0137fa1bb4a05856b0846d0743ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/*
 * 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 );
    }
}