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 ); /* The only way a file_query() call can fail is if its internal OS * file information gathering API (e.g. stat()) failed. If that * happens we should treat the file as if it no longer exists. We * then request the raw cached file_info_t structure for that file * and use the file name from there. */ file_info_t const * const ff = ffq ? ffq : file_info( 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, ff->name, 1 /* stat()'ed */, &ff->time ); } } }
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; }
int file_time( OBJECT * filename, time_t * time ) { file_info_t * ff = file_query( filename ); if ( !ff ) return -1; *time = ff->time; return 0; }
int file_collect_dir_content_( file_info_t * const d ) { LIST * files = L0; PATHNAME f; int n; STRUCT_DIRENT ** namelist; STRUCT_DIRENT * dirent; string path[ 1 ]; char const * dirstr; assert( d ); assert( d->is_dir ); assert( list_empty( d->files ) ); dirstr = object_str( d->name ); memset( (char *)&f, '\0', sizeof( f ) ); f.f_dir.ptr = dirstr; f.f_dir.len = strlen( dirstr ); if ( !*dirstr ) dirstr = "."; if ( -1 == ( n = scandir( dirstr, &namelist, NULL, alphasort ) ) ) return -1; string_new( path ); while ( n-- ) { OBJECT * name; dirent = namelist[ n ]; f.f_base.ptr = dirent->d_name #ifdef old_sinix - 2 /* Broken structure definition on sinix. */ #endif ; f.f_base.len = strlen( f.f_base.ptr ); string_truncate( path, 0 ); path_build( &f, path ); name = object_new( path->value ); /* Immediately stat the file to preserve invariants. */ if ( file_query( name ) ) files = list_push_back( files, name ); else object_free( name ); free( dirent ); } string_free( path ); free( namelist ); d->files = files; return 0; }
file_archive_info_t * file_archive_query( OBJECT * const path ) { int found; file_archive_info_t * const archive = file_archive_info( path, &found ); file_info_t * file = file_query( path ); if ( !( file && file->is_file ) ) { return 0; } archive->file = file; return archive; }
int file_is_file( OBJECT * filename ) { file_info_t * ff = file_query( filename ); if ( !ff ) return -1; return ff->is_file; }
void file_dirscan( OBJECT * dir, scanback func, void * closure ) { PROFILE_ENTER( FILE_DIRSCAN ); file_info_t * d = 0; d = file_query( dir ); if ( !d || !d->is_dir ) { PROFILE_EXIT( FILE_DIRSCAN ); return; } if ( ! d->files ) { LIST* files = L0; PATHNAME f; DIR *dd; STRUCT_DIRENT *dirent; string filename[1]; const char * dirstr = object_str( dir ); /* First enter directory itself */ memset( (char *)&f, '\0', sizeof( f ) ); f.f_dir.ptr = dirstr; f.f_dir.len = strlen( dirstr ); dirstr = *dirstr ? dirstr : "."; /* Now enter contents of directory. */ if ( !( dd = opendir( dirstr ) ) ) { PROFILE_EXIT( FILE_DIRSCAN ); return; } if ( DEBUG_BINDSCAN ) printf( "scan directory %s\n", dirstr ); string_new( filename ); while ( ( dirent = readdir( dd ) ) ) { OBJECT * filename_obj; # ifdef old_sinix /* Broken structure definition on sinix. */ f.f_base.ptr = dirent->d_name - 2; # else f.f_base.ptr = dirent->d_name; # endif f.f_base.len = strlen( f.f_base.ptr ); string_truncate( filename, 0 ); path_build( &f, filename, 0 ); filename_obj = object_new( filename->value ); files = list_new( files, filename_obj ); file_query( filename_obj ); } string_free( filename ); closedir( dd ); d->files = files; } /* Special case / : enter it */ { if ( strcmp( object_str( d->name ), "/" ) == 0 ) (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); } /* Now enter contents of directory */ if ( d->files ) { LIST * files = d->files; while ( files ) { file_info_t * ff = file_info( files->value ); (*func)( closure, ff->name, 1 /* stat()'ed */, ff->time ); files = list_next( files ); } } PROFILE_EXIT( FILE_DIRSCAN ); }
OBJECT * search( OBJECT * target, time_t *time, OBJECT * * another_target, int file ) { PATHNAME f[1]; LIST * varlist; string buf[1]; int found = 0; /* Will be set to 1 if target location is specified via LOCATE. */ int explicitly_located = 0; OBJECT * boundname = 0; OBJECT * varname; if ( another_target ) *another_target = 0; if (! explicit_bindings ) explicit_bindings = hashinit( sizeof(BINDING), "explicitly specified locations"); string_new( buf ); /* Parse the filename */ path_parse( object_str( target ), f ); f->f_grist.ptr = 0; f->f_grist.len = 0; varname = object_new( "LOCATE" ); varlist = var_get( varname ); object_free( varname ); if ( varlist ) { OBJECT * key; f->f_root.ptr = object_str( varlist->value ); f->f_root.len = strlen( object_str( varlist->value ) ); path_build( f, buf, 1 ); if ( DEBUG_SEARCH ) printf( "locate %s: %s\n", object_str( target ), buf->value ); explicitly_located = 1; key = object_new( buf->value ); timestamp( key, time ); object_free( key ); found = 1; } else if ( ( varname = object_new( "SEARCH" ), varlist = var_get( varname ), object_free( varname ), varlist ) ) { while ( varlist ) { BINDING b, *ba = &b; file_info_t *ff; OBJECT * key; OBJECT * test_path; f->f_root.ptr = object_str( varlist->value ); f->f_root.len = strlen( object_str( varlist->value ) ); string_truncate( buf, 0 ); path_build( f, buf, 1 ); if ( DEBUG_SEARCH ) printf( "search %s: %s\n", object_str( target ), buf->value ); test_path = object_new( buf->value ); key = path_as_key( test_path ); object_free( test_path ); ff = file_query( key ); timestamp( key, time ); b.binding = key; if ( hashcheck( explicit_bindings, (HASHDATA**)&ba ) ) { if ( DEBUG_SEARCH ) printf(" search %s: found explicitly located target %s\n", object_str( target ), object_str( ba->target ) ); if ( another_target ) *another_target = ba->target; found = 1; object_free( key ); break; } else if ( ff && ff->time ) { if ( !file || ff->is_file ) { found = 1; object_free( key ); break; } } object_free( key ); varlist = list_next( varlist ); } } if ( !found ) { /* Look for the obvious */ /* This is a questionable move. Should we look in the */ /* obvious place if SEARCH is set? */ OBJECT * key; f->f_root.ptr = 0; f->f_root.len = 0; string_truncate( buf, 0 ); path_build( f, buf, 1 ); if ( DEBUG_SEARCH ) printf( "search %s: %s\n", object_str( target ), buf->value ); key = object_new( buf->value ); timestamp( key, time ); object_free( key ); } boundname = object_new( buf->value ); string_free( buf ); if ( explicitly_located ) { BINDING b; BINDING * ba = &b; OBJECT * key = path_as_key( boundname ); b.binding = key; b.target = target; /* CONSIDER: we probably should issue a warning is another file is explicitly bound to the same location. This might break compatibility, though. */ if ( !hashenter( explicit_bindings, (HASHDATA * *)&ba ) ) { object_free( key ); } } /* prepare a call to BINDRULE if the variable is set */ call_bind_rule( target, boundname ); return boundname; }
void file_dirscan( char * dir, scanback func, void * closure ) { PROFILE_ENTER( FILE_DIRSCAN ); file_info_t * d = 0; dir = short_path_to_long_path( dir ); /* First enter directory itself */ d = file_query( dir ); if ( !d || !d->is_dir ) { PROFILE_EXIT( FILE_DIRSCAN ); return; } if ( !d->files ) { PATHNAME f; string filespec[ 1 ]; string filename[ 1 ]; long handle; int ret; struct _finddata_t finfo[ 1 ]; LIST * files = L0; int d_length = strlen( d->name ); memset( (char *)&f, '\0', sizeof( f ) ); f.f_dir.ptr = d->name; f.f_dir.len = d_length; /* Now enter contents of directory */ /* Prepare file search specification for the findfirst() API. */ if ( d_length == 0 ) string_copy( filespec, ".\\*" ); else { /* * We can not simply assume the given folder name will never include * its trailing path separator or otherwise we would not support the * Windows root folder specified without its drive letter, i.e. '\'. */ char trailingChar = d->name[ d_length - 1 ] ; string_copy( filespec, d->name ); if ( ( trailingChar != '\\' ) && ( trailingChar != '/' ) ) string_append( filespec, "\\" ); string_append( filespec, "*" ); } if ( DEBUG_BINDSCAN ) printf( "scan directory %s\n", dir ); #if defined(__BORLANDC__) && __BORLANDC__ < 0x550 if ( ret = findfirst( filespec->value, finfo, FA_NORMAL | FA_DIREC ) ) { string_free( filespec ); PROFILE_EXIT( FILE_DIRSCAN ); return; } string_new ( filename ); while ( !ret ) { file_info_t * ff = 0; f.f_base.ptr = finfo->ff_name; f.f_base.len = strlen( finfo->ff_name ); string_truncate( filename, 0 ); path_build( &f, filename ); files = list_new( files, newstr(filename->value) ); ff = file_info( filename->value ); ff->is_file = finfo->ff_attrib & FA_DIREC ? 0 : 1; ff->is_dir = finfo->ff_attrib & FA_DIREC ? 1 : 0; ff->size = finfo->ff_fsize; ff->time = (finfo->ff_ftime << 16) | finfo->ff_ftime; ret = findnext( finfo ); } # else handle = _findfirst( filespec->value, finfo ); if ( ret = ( handle < 0L ) ) { string_free( filespec ); PROFILE_EXIT( FILE_DIRSCAN ); return; } string_new( filename ); while ( !ret ) { file_info_t * ff = 0; f.f_base.ptr = finfo->name; f.f_base.len = strlen( finfo->name ); string_truncate( filename, 0 ); path_build( &f, filename, 0 ); files = list_new( files, newstr( filename->value ) ); ff = file_info( filename->value ); ff->is_file = finfo->attrib & _A_SUBDIR ? 0 : 1; ff->is_dir = finfo->attrib & _A_SUBDIR ? 1 : 0; ff->size = finfo->size; ff->time = finfo->time_write; ret = _findnext( handle, finfo ); } _findclose( handle ); # endif string_free( filename ); string_free( filespec ); d->files = files; } /* Special case \ or d:\ : enter it */ { unsigned long len = strlen(d->name); if ( len == 1 && d->name[0] == '\\' ) (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); else if ( len == 3 && d->name[1] == ':' ) { (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); /* We've just entered 3-letter drive name spelling (with trailing slash), into the hash table. Now enter two-letter variant, without trailing slash, so that if we try to check whether "c:" exists, we hit it. Jam core has workarounds for that. Given: x = c:\whatever\foo ; p = $(x:D) ; p2 = $(p:D) ; There will be no trailing slash in $(p), but there will be one in $(p2). But, that seems rather fragile. */ d->name[2] = 0; (*func)( closure, d->name, 1 /* stat()'ed */, d->time ); } } /* Now enter contents of directory */ if ( d->files ) { LIST * files = d->files; while ( files ) { file_info_t * ff = file_info( files->string ); (*func)( closure, ff->name, 1 /* stat()'ed */, ff->time ); files = list_next( files ); } } PROFILE_EXIT( FILE_DIRSCAN ); }
int file_is_file( OBJECT * const path ) { file_info_t const * const ff = file_query( path ); return ff ? ff->is_file : -1; }
LIST * path_exists( FRAME * frame, int flags ) { return file_query( list_front( lol_get( frame->args, 0 ) ) ) ? list_new( object_copy( constant_true ) ) : L0; }
OBJECT * search( OBJECT * target, time_t *time, OBJECT * * another_target, int file ) { PATHNAME f[1]; LIST * varlist; string buf[1]; int found = 0; /* Will be set to 1 if target location is specified via LOCATE. */ int explicitly_located = 0; OBJECT * boundname = 0; if ( another_target ) *another_target = 0; if (! explicit_bindings ) explicit_bindings = hashinit( sizeof(BINDING), "explicitly specified locations"); string_new( buf ); /* Parse the filename */ path_parse( object_str( target ), f ); f->f_grist.ptr = 0; f->f_grist.len = 0; varlist = var_get( root_module(), constant_LOCATE ); if ( !list_empty( varlist ) ) { OBJECT * key; f->f_root.ptr = object_str( list_front( varlist ) ); f->f_root.len = strlen( object_str( list_front( varlist ) ) ); path_build( f, buf, 1 ); if ( DEBUG_SEARCH ) printf( "locate %s: %s\n", object_str( target ), buf->value ); explicitly_located = 1; key = object_new( buf->value ); timestamp( key, time ); object_free( key ); found = 1; } else if ( varlist = var_get( root_module(), constant_SEARCH ), !list_empty( varlist ) ) { LISTITER iter = list_begin( varlist ), end = list_end( varlist ); for ( ; iter != end; iter = list_next( iter ) ) { BINDING * ba; file_info_t *ff; OBJECT * key; OBJECT * test_path; f->f_root.ptr = object_str( list_item( iter ) ); f->f_root.len = strlen( object_str( list_item( iter ) ) ); string_truncate( buf, 0 ); path_build( f, buf, 1 ); if ( DEBUG_SEARCH ) printf( "search %s: %s\n", object_str( target ), buf->value ); test_path = object_new( buf->value ); key = path_as_key( test_path ); object_free( test_path ); ff = file_query( key ); timestamp( key, time ); if ( ( ba = (BINDING *)hash_find( explicit_bindings, key ) ) ) { if ( DEBUG_SEARCH ) printf(" search %s: found explicitly located target %s\n", object_str( target ), object_str( ba->target ) ); if ( another_target ) *another_target = ba->target; found = 1; object_free( key ); break; } else if ( ff && ff->time ) { if ( !file || ff->is_file ) { found = 1; object_free( key ); break; } } object_free( key ); } } if ( !found ) { /* Look for the obvious */ /* This is a questionable move. Should we look in the */ /* obvious place if SEARCH is set? */ OBJECT * key; f->f_root.ptr = 0; f->f_root.len = 0; string_truncate( buf, 0 ); path_build( f, buf, 1 ); if ( DEBUG_SEARCH ) printf( "search %s: %s\n", object_str( target ), buf->value ); key = object_new( buf->value ); timestamp( key, time ); object_free( key ); } boundname = object_new( buf->value ); string_free( buf ); if ( explicitly_located ) { int found; BINDING * ba; OBJECT * key = path_as_key( boundname ); /* CONSIDER: we probably should issue a warning is another file is explicitly bound to the same location. This might break compatibility, though. */ ba = (BINDING *)hash_insert( explicit_bindings, key, &found ); if ( !found ) { ba->binding = key; ba->target = target; } else { object_free( key ); } } /* prepare a call to BINDRULE if the variable is set */ call_bind_rule( target, boundname ); return boundname; }
OBJECT * search( OBJECT * target, timestamp * const time, OBJECT * * another_target, int const file ) { PATHNAME f[ 1 ]; LIST * varlist; string buf[ 1 ]; int found = 0; OBJECT * boundname = 0; if ( another_target ) *another_target = 0; if ( !explicit_bindings ) explicit_bindings = hashinit( sizeof( BINDING ), "explicitly specified " "locations" ); string_new( buf ); /* Parse the filename. */ path_parse( object_str( target ), f ); f->f_grist.ptr = 0; f->f_grist.len = 0; varlist = var_get( root_module(), constant_LOCATE ); if ( !list_empty( varlist ) ) { OBJECT * key; f->f_root.ptr = object_str( list_front( varlist ) ); f->f_root.len = strlen( object_str( list_front( varlist ) ) ); path_build( f, buf ); if ( DEBUG_SEARCH ) printf( "locate %s: %s\n", object_str( target ), buf->value ); key = object_new( buf->value ); timestamp_from_path( time, key ); object_free( key ); found = 1; } else if ( varlist = var_get( root_module(), constant_SEARCH ), !list_empty( varlist ) ) { LISTITER iter = list_begin( varlist ); LISTITER const end = list_end( varlist ); for ( ; iter != end; iter = list_next( iter ) ) { BINDING * ba; file_info_t * ff; OBJECT * key; OBJECT * test_path; f->f_root.ptr = object_str( list_item( iter ) ); f->f_root.len = strlen( object_str( list_item( iter ) ) ); string_truncate( buf, 0 ); path_build( f, buf ); if ( DEBUG_SEARCH ) printf( "search %s: %s\n", object_str( target ), buf->value ); test_path = object_new( buf->value ); key = path_as_key( test_path ); object_free( test_path ); ff = file_query( key ); timestamp_from_path( time, key ); if ( ( ba = (BINDING *)hash_find( explicit_bindings, key ) ) ) { if ( DEBUG_SEARCH ) printf(" search %s: found explicitly located target %s\n", object_str( target ), object_str( ba->target ) ); if ( another_target ) *another_target = ba->target; found = 1; object_free( key ); break; } else if ( ff ) { if ( !file || ff->is_file ) { found = 1; object_free( key ); break; } } object_free( key ); } } if ( !found ) { /* Look for the obvious. */ /* This is a questionable move. Should we look in the obvious place if * SEARCH is set? */ OBJECT * key; f->f_root.ptr = 0; f->f_root.len = 0; string_truncate( buf, 0 ); path_build( f, buf ); if ( DEBUG_SEARCH ) printf( "search %s: %s\n", object_str( target ), buf->value ); key = object_new( buf->value ); timestamp_from_path( time, key ); object_free( key ); } boundname = object_new( buf->value ); string_free( buf ); /* Prepare a call to BINDRULE if the variable is set. */ call_bind_rule( target, boundname ); return boundname; }