File size: 9,717 Bytes
158b61b |
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 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
/*
* Copyright 2001-2004 David Abrahams.
* Copyright 2005 Rene Rivera.
* 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)
*/
/*
* filesys.c - OS independant file system manipulation support
*
* External routines:
* file_build1() - construct a path string based on PATHNAME information
* file_dirscan() - scan a directory for files
* file_done() - module cleanup called on shutdown
* file_info() - return cached information about a path
* file_is_file() - return whether a path identifies an existing file
* file_query() - get cached information about a path, query the OS if
* needed
* file_remove_atexit() - schedule a path to be removed on program exit
* file_time() - get a file timestamp
*
* External routines - utilites for OS specific module implementations:
* file_query_posix_() - query information about a path using POSIX stat()
*
* Internal routines:
* file_dirscan_impl() - no-profiling worker for file_dirscan()
*/
#include "jam.h"
#include "filesys.h"
#include "lists.h"
#include "object.h"
#include "pathsys.h"
#include "strings.h"
#include <assert.h>
#include <sys/stat.h>
/* Internal OS specific implementation details - have names ending with an
* underscore and are expected to be implemented in an OS specific fileXXX.c
* module.
*/
void file_dirscan_( file_info_t * const dir, scanback func, void * closure );
int file_collect_dir_content_( file_info_t * const dir );
void file_query_( file_info_t * const );
static void file_dirscan_impl( OBJECT * dir, scanback func, void * closure );
static void free_file_info( void * xfile, void * data );
static void remove_files_atexit( void );
static struct hash * filecache_hash;
/*
* file_build1() - construct a path string based on PATHNAME information
*/
void file_build1( PATHNAME * const f, string * file )
{
if ( DEBUG_SEARCH )
{
printf( "build file: " );
if ( f->f_root.len )
printf( "root = '%.*s' ", f->f_root.len, f->f_root.ptr );
if ( f->f_dir.len )
printf( "dir = '%.*s' ", f->f_dir.len, f->f_dir.ptr );
if ( f->f_base.len )
printf( "base = '%.*s' ", f->f_base.len, f->f_base.ptr );
printf( "\n" );
}
/* Start with the grist. If the current grist is not surrounded by <>'s, add
* them.
*/
if ( f->f_grist.len )
{
if ( f->f_grist.ptr[ 0 ] != '<' )
string_push_back( file, '<' );
string_append_range(
file, f->f_grist.ptr, f->f_grist.ptr + f->f_grist.len );
if ( file->value[ file->size - 1 ] != '>' )
string_push_back( file, '>' );
}
}
/*
* file_dirscan() - scan a directory for files
*/
void file_dirscan( OBJECT * dir, scanback func, void * closure )
{
PROFILE_ENTER( FILE_DIRSCAN );
file_dirscan_impl( dir, func, closure );
PROFILE_EXIT( FILE_DIRSCAN );
}
/*
* file_done() - module cleanup called on shutdown
*/
void file_done()
{
remove_files_atexit();
if ( filecache_hash )
{
hashenumerate( filecache_hash, free_file_info, (void *)0 );
hashdone( filecache_hash );
}
}
/*
* file_info() - return cached information about a path
*
* Returns a default initialized structure containing only the path's normalized
* name in case this is the first time this file system entity has been
* referenced.
*/
file_info_t * file_info( OBJECT * const path, int * found )
{
OBJECT * const path_key = path_as_key( path );
file_info_t * finfo;
if ( !filecache_hash )
filecache_hash = hashinit( sizeof( file_info_t ), "file_info" );
finfo = (file_info_t *)hash_insert( filecache_hash, path_key, found );
if ( !*found )
{
finfo->name = path_key;
finfo->files = L0;
}
else
object_free( path_key );
return finfo;
}
/*
* file_is_file() - return whether a path identifies an existing file
*/
int file_is_file( OBJECT * const path )
{
file_info_t const * const ff = file_query( path );
return ff ? ff->is_file : -1;
}
/*
* file_time() - get a file timestamp
*/
int file_time( OBJECT * const path, timestamp * const time )
{
file_info_t const * const ff = file_query( path );
if ( !ff ) return -1;
timestamp_copy( time, &ff->time );
return 0;
}
/*
* file_query() - get cached information about a path, query the OS if needed
*
* Returns 0 in case querying the OS about the given path fails, e.g. because
* the path does not reference an existing file system object.
*/
file_info_t * file_query( OBJECT * const path )
{
/* FIXME: Add tracking for disappearing files (i.e. those that can not be
* detected by stat() even though they had been detected successfully
* before) and see how they should be handled in the rest of Boost Jam code.
* Possibly allow Jamfiles to specify some files as 'volatile' which would
* make Boost Jam avoid caching information about those files and instead
* ask the OS about them every time.
*/
int found;
file_info_t * const ff = file_info( path, &found );
if ( !found )
{
file_query_( ff );
if ( ff->exists )
{
/* Set the path's timestamp to 1 in case it is 0 or undetected to avoid
* confusion with non-existing paths.
*/
if ( timestamp_empty( &ff->time ) )
timestamp_init( &ff->time, 1, 0 );
}
}
if ( !ff->exists )
{
return 0;
}
return ff;
}
/*
* file_query_posix_() - query information about a path using POSIX stat()
*
* Fallback file_query_() implementation for OS specific modules.
*
* Note that the Windows POSIX stat() function implementation suffers from
* several issues:
* * Does not support file timestamps with resolution finer than 1 second,
* meaning it can not be used to detect file timestamp changes of less than
* 1 second. One possible consequence is that some fast-paced touch commands
* (such as those done by Boost Build's internal testing system if it does
* not do some extra waiting) will not be detected correctly by the build
* system.
* * Returns file modification times automatically adjusted for daylight
* savings time even though daylight savings time should have nothing to do
* with internal time representation.
*/
void file_query_posix_( file_info_t * const info )
{
struct stat statbuf;
char const * const pathstr = object_str( info->name );
char const * const pathspec = *pathstr ? pathstr : ".";
if ( stat( pathspec, &statbuf ) < 0 )
{
info->is_file = 0;
info->is_dir = 0;
info->exists = 0;
timestamp_clear( &info->time );
}
else
{
info->is_file = statbuf.st_mode & S_IFREG ? 1 : 0;
info->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0;
info->exists = 1;
timestamp_init( &info->time, statbuf.st_mtime, 0 );
}
}
/*
* file_remove_atexit() - schedule a path to be removed on program exit
*/
static LIST * files_to_remove = L0;
void file_remove_atexit( OBJECT * const path )
{
files_to_remove = list_push_back( files_to_remove, object_copy( path ) );
}
/*
* file_dirscan_impl() - no-profiling worker for file_dirscan()
*/
static void file_dirscan_impl( OBJECT * dir, scanback func, void * closure )
{
file_info_t * const d = file_query( dir );
if ( !d || !d->is_dir )
return;
/* Lazy collect the directory content information. */
if ( list_empty( d->files ) )
{
if ( DEBUG_BINDSCAN )
printf( "scan directory %s\n", object_str( d->name ) );
if ( file_collect_dir_content_( d ) < 0 )
return;
}
/* OS specific part of the file_dirscan operation. */
file_dirscan_( d, func, closure );
/* Report the collected directory content. */
{
LISTITER iter = list_begin( d->files );
LISTITER const end = list_end( d->files );
for ( ; iter != end; iter = list_next( iter ) )
{
OBJECT * const path = list_item( iter );
file_info_t const * const ffq = file_query( path );
/* Using a file name read from a file_info_t structure allows OS
* specific implementations to store some kind of a normalized file
* name there. Using such a normalized file name then allows us to
* correctly recognize different file paths actually identifying the
* same file. For instance, an implementation may:
* - convert all file names internally to lower case on a case
* insensitive file system
* - convert the NTFS paths to their long path variants as that
* file system each file system entity may have a long and a
* short path variant thus allowing for many different path
* strings identifying the same file.
*/
(*func)( closure, ffq->name, 1 /* stat()'ed */, &ffq->time );
}
}
}
static void free_file_info( void * xfile, void * data )
{
file_info_t * const file = (file_info_t *)xfile;
object_free( file->name );
list_free( file->files );
}
static void remove_files_atexit( void )
{
LISTITER iter = list_begin( files_to_remove );
LISTITER const end = list_end( files_to_remove );
for ( ; iter != end; iter = list_next( iter ) )
remove( object_str( list_item( iter ) ) );
list_free( files_to_remove );
files_to_remove = L0;
}
|