/* * 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. * 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) */ /* * fileunix.c - manipulate file names and scan directories on UNIX/AmigaOS * * External routines: * file_archscan() - scan an archive for files * file_mkdir() - create a directory * file_supported_fmt_resolution() - file modification timestamp resolution * * External routines called only via routines in filesys.c: * file_collect_dir_content_() - collects directory content information * file_dirscan_() - OS specific file_dirscan() implementation * file_query_() - query information about a path from the OS */ #include "jam.h" #ifdef USE_FILEUNIX #include "filesys.h" #include "object.h" #include "pathsys.h" #include "strings.h" #include #include #include /* needed for mkdir() */ #if defined( sun ) || defined( __sun ) || defined( linux ) # include /* needed for read and close prototype */ #endif #if defined( OS_SEQUENT ) || \ defined( OS_DGUX ) || \ defined( OS_SCO ) || \ defined( OS_ISC ) # define PORTAR 1 #endif #if defined( OS_RHAPSODY ) || defined( OS_MACOSX ) || defined( OS_NEXT ) # include # include /* need unistd for rhapsody's proper lseek */ # define STRUCT_DIRENT struct direct #else # include # define STRUCT_DIRENT struct dirent #endif #ifdef OS_COHERENT # include # define HAVE_AR #endif #if defined( OS_MVS ) || defined( OS_INTERIX ) #define ARMAG "!\n" #define SARMAG 8 #define ARFMAG "`\n" #define HAVE_AR struct ar_hdr /* archive file member header - printable ascii */ { char ar_name[ 16 ]; /* file member name - `/' terminated */ char ar_date[ 12 ]; /* file member date - decimal */ char ar_uid[ 6 ]; /* file member user id - decimal */ char ar_gid[ 6 ]; /* file member group id - decimal */ char ar_mode[ 8 ]; /* file member mode - octal */ char ar_size[ 10 ]; /* file member size - decimal */ char ar_fmag[ 2 ]; /* ARFMAG - string to end header */ }; #endif #if defined( OS_QNX ) || \ defined( OS_BEOS ) || \ defined( OS_HAIKU ) || \ defined( OS_MPEIX ) # define NO_AR # define HAVE_AR #endif #ifndef HAVE_AR # ifdef OS_AIX /* Define these for AIX to get the definitions for both small and big archive * file format variants. */ # define __AR_SMALL__ # define __AR_BIG__ # endif # include #endif /* * file_collect_dir_content_() - collects directory content information */ int file_collect_dir_content_( file_info_t * const d ) { LIST * files = L0; PATHNAME f; DIR * dd; 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 ( !( dd = opendir( dirstr ) ) ) return -1; string_new( path ); while ( ( dirent = readdir( dd ) ) ) { OBJECT * name; 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 ); } string_free( path ); closedir( dd ); d->files = files; return 0; } /* * file_dirscan_() - OS specific file_dirscan() implementation */ void file_dirscan_( file_info_t * const d, scanback func, void * closure ) { assert( d ); assert( d->is_dir ); /* Special case / : enter it */ if ( !strcmp( object_str( d->name ), "/" ) ) (*func)( closure, d->name, 1 /* stat()'ed */, &d->time ); } /* * file_mkdir() - create a directory */ int file_mkdir( char const * const path ) { /* Explicit cast to remove const modifiers and avoid related compiler * warnings displayed when using the intel compiler. */ return mkdir( (char *)path, 0777 ); } /* * file_query_() - query information about a path from the OS */ void file_query_( file_info_t * const info ) { file_query_posix_( info ); } /* * file_supported_fmt_resolution() - file modification timestamp resolution * * Returns the minimum file modification timestamp resolution supported by this * Boost Jam implementation. File modification timestamp changes of less than * the returned value might not be recognized. * * Does not take into consideration any OS or file system related restrictions. * * Return value 0 indicates that any value supported by the OS is also supported * here. */ void file_supported_fmt_resolution( timestamp * const t ) { /* The current implementation does not support file modification timestamp * resolution of less than one second. */ timestamp_init( t, 1, 0 ); } /* * file_archscan() - scan an archive for files */ #ifndef AIAMAG /* God-fearing UNIX */ #define SARFMAG 2 #define SARHDR sizeof( struct ar_hdr ) void file_archscan( char const * archive, scanback func, void * closure ) { #ifndef NO_AR struct ar_hdr ar_hdr; char * string_table = 0; char buf[ MAXJPATH ]; long offset; int fd; if ( ( fd = open( archive, O_RDONLY, 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 ) #ifdef ARFZMAG /* OSF also has a compressed format */ && memcmp( ar_hdr.ar_fmag, ARFZMAG, SARFMAG ) #endif ) ) { char lar_name_[ 257 ]; char * lar_name = lar_name_ + 1; long lar_date; long lar_size; long lar_offset; char * c; char * src; char * dest; strncpy( lar_name, ar_hdr.ar_name, sizeof( ar_hdr.ar_name ) ); sscanf( ar_hdr.ar_date, "%ld", &lar_date ); sscanf( ar_hdr.ar_size, "%ld", &lar_size ); if ( ar_hdr.ar_name[ 0 ] == '/' ) { if ( ar_hdr.ar_name[ 1 ] == '/' ) { /* This is the "string table" entry of the symbol table, holding * filename strings longer than 15 characters, i.e. those that * do not fit into ar_name. */ string_table = (char *)BJAM_MALLOC_ATOMIC( lar_size ); lseek( fd, offset + SARHDR, 0 ); if ( read( fd, string_table, lar_size ) != lar_size ) printf("error reading string table\n"); } else if ( string_table && 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. */ dest = lar_name; lar_offset = atoi( lar_name + 1 ); src = &string_table[ lar_offset ]; while ( *src != '/' ) *dest++ = *src++; *dest = '/'; } } c = lar_name - 1; while ( ( *++c != ' ' ) && ( *c != '/' ) ); *c = '\0'; if ( DEBUG_BINDSCAN ) printf( "archive name %s found\n", lar_name ); sprintf( buf, "%s(%s)", archive, lar_name ); { OBJECT * const member = object_new( buf ); timestamp time; timestamp_init( &time, (time_t)lar_date, 0 ); (*func)( closure, member, 1 /* time valid */, &time ); object_free( member ); } offset += SARHDR + ( ( lar_size + 1 ) & ~1 ); lseek( fd, offset, 0 ); } if ( string_table ) BJAM_FREE( string_table ); close( fd ); #endif /* NO_AR */ } #else /* AIAMAG - RS6000 AIX */ static void file_archscan_small( int fd, char const * archive, scanback func, void * closure ) { struct fl_hdr fl_hdr; struct { struct ar_hdr hdr; char pad[ 256 ]; } ar_hdr ; char buf[ MAXJPATH ]; long offset; if ( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ ) return; sscanf( fl_hdr.fl_fstmoff, "%ld", &offset ); if ( DEBUG_BINDSCAN ) printf( "scan archive %s\n", archive ); while ( offset > 0 && lseek( fd, offset, 0 ) >= 0 && read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= (int)sizeof( ar_hdr.hdr ) ) { long lar_date; int lar_namlen; sscanf( ar_hdr.hdr.ar_namlen, "%d" , &lar_namlen ); sscanf( ar_hdr.hdr.ar_date , "%ld", &lar_date ); sscanf( ar_hdr.hdr.ar_nxtmem, "%ld", &offset ); if ( !lar_namlen ) continue; ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0'; sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name ); { OBJECT * const member = object_new( buf ); timestamp time; timestamp_init( &time, (time_t)lar_date, 0 ); (*func)( closure, member, 1 /* time valid */, &time ); object_free( member ); } } } /* Check for OS versions supporting the big variant. */ #ifdef AR_HSZ_BIG static void file_archscan_big( int fd, char const * archive, scanback func, void * closure ) { struct fl_hdr_big fl_hdr; struct { struct ar_hdr_big hdr; char pad[ 256 ]; } ar_hdr ; char buf[ MAXJPATH ]; long long offset; if ( read( fd, (char *)&fl_hdr, FL_HSZ_BIG ) != FL_HSZ_BIG ) return; sscanf( fl_hdr.fl_fstmoff, "%lld", &offset ); if ( DEBUG_BINDSCAN ) printf( "scan archive %s\n", archive ); while ( offset > 0 && lseek( fd, offset, 0 ) >= 0 && read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= sizeof( ar_hdr.hdr ) ) { long lar_date; int lar_namlen; sscanf( ar_hdr.hdr.ar_namlen, "%d" , &lar_namlen ); sscanf( ar_hdr.hdr.ar_date , "%ld" , &lar_date ); sscanf( ar_hdr.hdr.ar_nxtmem, "%lld", &offset ); if ( !lar_namlen ) continue; ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0'; sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name ); { OBJECT * const member = object_new( buf ); timestamp time; timestamp_init( &time, (time_t)lar_date, 0 ); (*func)( closure, member, 1 /* time valid */, &time ); object_free( member ); } } } #endif /* AR_HSZ_BIG */ void file_archscan( char const * archive, scanback func, void * closure ) { int fd; char fl_magic[ SAIAMAG ]; if ( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 ) return; if ( read( fd, fl_magic, SAIAMAG ) != SAIAMAG || lseek( fd, 0, SEEK_SET ) == -1 ) { close( fd ); return; } if ( !strncmp( AIAMAG, fl_magic, SAIAMAG ) ) { /* read small variant */ file_archscan_small( fd, archive, func, closure ); } #ifdef AR_HSZ_BIG else if ( !strncmp( AIAMAGBIG, fl_magic, SAIAMAG ) ) { /* read big variant */ file_archscan_big( fd, archive, func, closure ); } #endif close( fd ); } #endif /* AIAMAG - RS6000 AIX */ #endif /* USE_FILEUNIX */