|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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" ); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, '>' ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void file_dirscan( OBJECT * dir, scanback func, void * closure ) |
|
|
{ |
|
|
PROFILE_ENTER( FILE_DIRSCAN ); |
|
|
file_dirscan_impl( dir, func, closure ); |
|
|
PROFILE_EXIT( FILE_DIRSCAN ); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void file_done() |
|
|
{ |
|
|
remove_files_atexit(); |
|
|
if ( filecache_hash ) |
|
|
{ |
|
|
hashenumerate( filecache_hash, free_file_info, (void *)0 ); |
|
|
hashdone( filecache_hash ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int file_is_file( OBJECT * const path ) |
|
|
{ |
|
|
file_info_t const * const ff = file_query( path ); |
|
|
return ff ? ff->is_file : -1; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_info_t * file_query( OBJECT * const path ) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int found; |
|
|
file_info_t * const ff = file_info( path, &found ); |
|
|
if ( !found ) |
|
|
{ |
|
|
file_query_( ff ); |
|
|
if ( ff->exists ) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
if ( timestamp_empty( &ff->time ) ) |
|
|
timestamp_init( &ff->time, 1, 0 ); |
|
|
} |
|
|
} |
|
|
if ( !ff->exists ) |
|
|
{ |
|
|
return 0; |
|
|
} |
|
|
return ff; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 ) ); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
file_dirscan_( d, func, closure ); |
|
|
|
|
|
|
|
|
{ |
|
|
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 ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(*func)( closure, ffq->name, 1 , &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; |
|
|
} |
|
|
|