diff options
Diffstat (limited to 'jam-files/engine/filent.c')
-rw-r--r-- | jam-files/engine/filent.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/jam-files/engine/filent.c b/jam-files/engine/filent.c new file mode 100644 index 000000000..ab1895767 --- /dev/null +++ b/jam-files/engine/filent.c @@ -0,0 +1,387 @@ +/* + * Copyright 1993, 1995 Christopher Seiwald. + * + * This file is part of Jam - see jam.c for Copyright information. + */ + +/* This file is ALSO: + * 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) + */ + +# include "jam.h" + +# include "filesys.h" +# include "pathsys.h" +# include "strings.h" +# include "newstr.h" + +# ifdef OS_NT + +# ifdef __BORLANDC__ +# if __BORLANDC__ < 0x550 +# include <dir.h> +# include <dos.h> +# endif +# undef FILENAME /* cpp namespace collision */ +# define _finddata_t ffblk +# endif + +# include <io.h> +# include <sys/stat.h> +# include <ctype.h> +# include <direct.h> + +/* + * filent.c - scan directories and archives on NT + * + * External routines: + * + * file_dirscan() - scan a directory for files + * file_time() - get timestamp of file, if not done by file_dirscan() + * file_archscan() - scan an archive for files + * + * File_dirscan() and file_archscan() call back a caller provided function + * for each file found. A flag to this callback function lets file_dirscan() + * and file_archscan() indicate that a timestamp is being provided with the + * file. If file_dirscan() or file_archscan() do not provide the file's + * timestamp, interested parties may later call file_time(). + * + * 07/10/95 (taylor) Findfirst() returns the first file on NT. + * 05/03/96 (seiwald) split apart into pathnt.c + */ + +/* + * file_dirscan() - scan a directory for files + */ + +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 ); +} + +file_info_t * file_query( char * filename ) +{ + file_info_t * ff = file_info( filename ); + if ( ! ff->time ) + { + struct stat statbuf; + + if ( stat( *filename ? filename : ".", &statbuf ) < 0 ) + return 0; + + ff->is_file = statbuf.st_mode & S_IFREG ? 1 : 0; + ff->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0; + ff->size = statbuf.st_size; + ff->time = statbuf.st_mtime ? statbuf.st_mtime : 1; + } + return ff; +} + +/* + * file_time() - get timestamp of file, if not done by file_dirscan() + */ + +int +file_time( + char *filename, + time_t *time ) +{ + file_info_t * ff = file_query( filename ); + if ( !ff ) return -1; + *time = ff->time; + return 0; +} + +int file_is_file(char* filename) +{ + file_info_t * ff = file_query( filename ); + if ( !ff ) return -1; + return ff->is_file; +} + +int file_mkdir(char *pathname) +{ + return _mkdir(pathname); +} + +/* + * file_archscan() - scan an archive for files + */ + +/* Straight from SunOS */ + +#define ARMAG "!<arch>\n" +#define SARMAG 8 + +#define ARFMAG "`\n" + +struct ar_hdr { + char ar_name[16]; + char ar_date[12]; + char ar_uid[6]; + char ar_gid[6]; + char ar_mode[8]; + char ar_size[10]; + char ar_fmag[2]; +}; + +# define SARFMAG 2 +# define SARHDR sizeof( struct ar_hdr ) + +void +file_archscan( + char *archive, + scanback func, + void *closure ) +{ + struct ar_hdr ar_hdr; + char *string_table = 0; + char buf[ MAXJPATH ]; + long offset; + int fd; + + if ( ( fd = open( archive, O_RDONLY | O_BINARY, 0 ) ) < 0 ) + return; + + if ( read( fd, buf, SARMAG ) != SARMAG || + strncmp( ARMAG, buf, SARMAG ) ) + { + close( fd ); + return; + } + + offset = SARMAG; + + if ( DEBUG_BINDSCAN ) + printf( "scan archive %s\n", archive ); + + while ( ( read( fd, &ar_hdr, SARHDR ) == SARHDR ) && + !memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG ) ) + { + long lar_date; + long lar_size; + char *name = 0; + char *endname; + char *c; + + sscanf( ar_hdr.ar_date, "%ld", &lar_date ); + sscanf( ar_hdr.ar_size, "%ld", &lar_size ); + + lar_size = ( lar_size + 1 ) & ~1; + + if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] == '/' ) + { + /* this is the "string table" entry of the symbol table, + ** which holds strings of filenames that are longer than + ** 15 characters (ie. don't fit into a ar_name + */ + + string_table = BJAM_MALLOC_ATOMIC(lar_size+1); + if (read(fd, string_table, lar_size) != lar_size) + printf("error reading string table\n"); + string_table[lar_size] = '\0'; + offset += SARHDR + lar_size; + continue; + } + else if (ar_hdr.ar_name[0] == '/' && ar_hdr.ar_name[1] != ' ') + { + /* Long filenames are recognized by "/nnnn" where nnnn is + ** the offset of the string in the string table represented + ** in ASCII decimals. + */ + + name = string_table + atoi( ar_hdr.ar_name + 1 ); + for ( endname = name; *endname && *endname != '\n'; ++endname) {} + } + else + { + /* normal name */ + name = ar_hdr.ar_name; + endname = name + sizeof( ar_hdr.ar_name ); + } + + /* strip trailing white-space, slashes, and backslashes */ + + while ( endname-- > name ) + if ( !isspace(*endname) && ( *endname != '\\' ) && ( *endname != '/' ) ) + break; + *++endname = 0; + + /* strip leading directory names, an NT specialty */ + + if ( c = strrchr( name, '/' ) ) + name = c + 1; + if ( c = strrchr( name, '\\' ) ) + name = c + 1; + + sprintf( buf, "%s(%.*s)", archive, endname - name, name ); + (*func)( closure, buf, 1 /* time valid */, (time_t)lar_date ); + + offset += SARHDR + lar_size; + lseek( fd, offset, 0 ); + } + + close( fd ); +} + +# endif /* NT */ |