1 /*
2  * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
3  *
4  * This file is part of Jam - see jam.c for Copyright information.
5  */
6 
7 /*  This file is ALSO:
8  *  Copyright 2001-2004 David Abrahams.
9  *  Copyright 2005 Rene Rivera.
10  *  Distributed under the Boost Software License, Version 1.0.
11  *  (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
12  */
13 
14 /*
15  * fileunix.c - manipulate file names and scan directories on UNIX/AmigaOS
16  *
17  * External routines:
18  *  file_archscan()                 - scan an archive for files
19  *  file_mkdir()                    - create a directory
20  *  file_supported_fmt_resolution() - file modification timestamp resolution
21  *
22  * External routines called only via routines in filesys.c:
23  *  file_collect_dir_content_() - collects directory content information
24  *  file_dirscan_()             - OS specific file_dirscan() implementation
25  *  file_query_()               - query information about a path from the OS
26  */
27 
28 #include "jam.h"
29 #ifdef USE_FILEUNIX
30 #include "filesys.h"
31 
32 #include "object.h"
33 #include "pathsys.h"
34 #include "strings.h"
35 #include "output.h"
36 
37 #include <assert.h>
38 #include <stdio.h>
39 #include <sys/stat.h>  /* needed for mkdir() */
40 
41 #if defined( sun ) || defined( __sun ) || defined( linux )
42 # include <unistd.h>  /* needed for read and close prototype */
43 #endif
44 
45 #if defined( OS_SEQUENT ) || \
46     defined( OS_DGUX ) || \
47     defined( OS_SCO ) || \
48     defined( OS_ISC )
49 # define PORTAR 1
50 #endif
51 
52 #if defined( OS_RHAPSODY ) || defined( OS_MACOSX ) || defined( OS_NEXT )
53 # include <sys/dir.h>
54 # include <unistd.h>  /* need unistd for rhapsody's proper lseek */
55 # define STRUCT_DIRENT struct direct
56 #else
57 # include <dirent.h>
58 # define STRUCT_DIRENT struct dirent
59 #endif
60 
61 #ifdef OS_COHERENT
62 # include <arcoff.h>
63 # define HAVE_AR
64 #endif
65 
66 #if defined( OS_MVS ) || defined( OS_INTERIX )
67 #define ARMAG  "!<arch>\n"
68 #define SARMAG  8
69 #define ARFMAG  "`\n"
70 #define HAVE_AR
71 
72 struct ar_hdr  /* archive file member header - printable ascii */
73 {
74     char ar_name[ 16 ];  /* file member name - `/' terminated */
75     char ar_date[ 12 ];  /* file member date - decimal */
76     char ar_uid[ 6 ];    /* file member user id - decimal */
77     char ar_gid[ 6 ];    /* file member group id - decimal */
78     char ar_mode[ 8 ];   /* file member mode - octal */
79     char ar_size[ 10 ];  /* file member size - decimal */
80     char ar_fmag[ 2 ];   /* ARFMAG - string to end header */
81 };
82 #endif
83 
84 #if defined( OS_QNX ) || \
85     defined( OS_BEOS ) || \
86     defined( OS_HAIKU ) || \
87     defined( OS_MPEIX )
88 # define NO_AR
89 # define HAVE_AR
90 #endif
91 
92 #ifndef HAVE_AR
93 # ifdef OS_AIX
94 /* Define these for AIX to get the definitions for both small and big archive
95  * file format variants.
96  */
97 #  define __AR_SMALL__
98 #  define __AR_BIG__
99 # endif
100 # include <ar.h>
101 #endif
102 
103 
104 /*
105  * file_collect_dir_content_() - collects directory content information
106  */
107 
file_collect_dir_content_(file_info_t * const d)108 int file_collect_dir_content_( file_info_t * const d )
109 {
110     LIST * files = L0;
111     PATHNAME f;
112     DIR * dd;
113     STRUCT_DIRENT * dirent;
114     string path[ 1 ];
115     char const * dirstr;
116 
117     assert( d );
118     assert( d->is_dir );
119     assert( list_empty( d->files ) );
120 
121     dirstr = object_str( d->name );
122 
123     memset( (char *)&f, '\0', sizeof( f ) );
124     f.f_dir.ptr = dirstr;
125     f.f_dir.len = strlen( dirstr );
126 
127     if ( !*dirstr ) dirstr = ".";
128 
129     if ( !( dd = opendir( dirstr ) ) )
130         return -1;
131 
132     string_new( path );
133     while ( ( dirent = readdir( dd ) ) )
134     {
135         OBJECT * name;
136         f.f_base.ptr = dirent->d_name
137         #ifdef old_sinix
138             - 2  /* Broken structure definition on sinix. */
139         #endif
140             ;
141         f.f_base.len = strlen( f.f_base.ptr );
142 
143         string_truncate( path, 0 );
144         path_build( &f, path );
145         name = object_new( path->value );
146         /* Immediately stat the file to preserve invariants. */
147         if ( file_query( name ) )
148             files = list_push_back( files, name );
149         else
150             object_free( name );
151     }
152     string_free( path );
153 
154     closedir( dd );
155 
156     d->files = files;
157     return 0;
158 }
159 
160 
161 /*
162  * file_dirscan_() - OS specific file_dirscan() implementation
163  */
164 
file_dirscan_(file_info_t * const d,scanback func,void * closure)165 void file_dirscan_( file_info_t * const d, scanback func, void * closure )
166 {
167     assert( d );
168     assert( d->is_dir );
169 
170     /* Special case / : enter it */
171     if ( !strcmp( object_str( d->name ), "/" ) )
172         (*func)( closure, d->name, 1 /* stat()'ed */, &d->time );
173 }
174 
175 
176 /*
177  * file_mkdir() - create a directory
178  */
179 
file_mkdir(char const * const path)180 int file_mkdir( char const * const path )
181 {
182     /* Explicit cast to remove const modifiers and avoid related compiler
183      * warnings displayed when using the intel compiler.
184      */
185     return mkdir( (char *)path, 0777 );
186 }
187 
188 
189 /*
190  * file_query_() - query information about a path from the OS
191  */
192 
file_query_(file_info_t * const info)193 void file_query_( file_info_t * const info )
194 {
195     file_query_posix_( info );
196 }
197 
198 
199 /*
200  * file_supported_fmt_resolution() - file modification timestamp resolution
201  *
202  * Returns the minimum file modification timestamp resolution supported by this
203  * Boost Jam implementation. File modification timestamp changes of less than
204  * the returned value might not be recognized.
205  *
206  * Does not take into consideration any OS or file system related restrictions.
207  *
208  * Return value 0 indicates that any value supported by the OS is also supported
209  * here.
210  */
211 
file_supported_fmt_resolution(timestamp * const t)212 void file_supported_fmt_resolution( timestamp * const t )
213 {
214     /* The current implementation does not support file modification timestamp
215      * resolution of less than one second.
216      */
217     timestamp_init( t, 1, 0 );
218 }
219 
220 
221 /*
222  * file_archscan() - scan an archive for files
223  */
224 
225 #ifndef AIAMAG  /* God-fearing UNIX */
226 
227 #define SARFMAG  2
228 #define SARHDR  sizeof( struct ar_hdr )
229 
file_archscan(char const * archive,scanback func,void * closure)230 void file_archscan( char const * archive, scanback func, void * closure )
231 {
232 #ifndef NO_AR
233     struct ar_hdr ar_hdr;
234     char * string_table = 0;
235     char buf[ MAXJPATH ];
236     long offset;
237     int fd;
238 
239     if ( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 )
240         return;
241 
242     if ( read( fd, buf, SARMAG ) != SARMAG ||
243         strncmp( ARMAG, buf, SARMAG ) )
244     {
245         close( fd );
246         return;
247     }
248 
249     offset = SARMAG;
250 
251     if ( DEBUG_BINDSCAN )
252         out_printf( "scan archive %s\n", archive );
253 
254     while ( ( read( fd, &ar_hdr, SARHDR ) == SARHDR ) &&
255         !( memcmp( ar_hdr.ar_fmag, ARFMAG, SARFMAG )
256 #ifdef ARFZMAG
257             /* OSF also has a compressed format */
258             && memcmp( ar_hdr.ar_fmag, ARFZMAG, SARFMAG )
259 #endif
260         ) )
261     {
262         char   lar_name_[ 257 ];
263         char * lar_name = lar_name_ + 1;
264         long   lar_date;
265         long   lar_size;
266         long   lar_offset;
267         char * c;
268         char * src;
269         char * dest;
270 
271         strncpy( lar_name, ar_hdr.ar_name, sizeof( ar_hdr.ar_name ) );
272 
273         sscanf( ar_hdr.ar_date, "%ld", &lar_date );
274         sscanf( ar_hdr.ar_size, "%ld", &lar_size );
275 
276         if ( ar_hdr.ar_name[ 0 ] == '/' )
277         {
278             if ( ar_hdr.ar_name[ 1 ] == '/' )
279             {
280                 /* This is the "string table" entry of the symbol table, holding
281                  * filename strings longer than 15 characters, i.e. those that
282                  * do not fit into ar_name.
283                  */
284                 string_table = (char *)BJAM_MALLOC_ATOMIC( lar_size );
285                 lseek( fd, offset + SARHDR, 0 );
286                 if ( read( fd, string_table, lar_size ) != lar_size )
287                     out_printf("error reading string table\n");
288             }
289             else if ( string_table && ar_hdr.ar_name[ 1 ] != ' ' )
290             {
291                 /* Long filenames are recognized by "/nnnn" where nnnn is the
292                  * offset of the string in the string table represented in ASCII
293                  * decimals.
294                  */
295                 dest = lar_name;
296                 lar_offset = atoi( lar_name + 1 );
297                 src = &string_table[ lar_offset ];
298                 while ( *src != '/' )
299                     *dest++ = *src++;
300                 *dest = '/';
301             }
302         }
303 
304         c = lar_name - 1;
305         while ( ( *++c != ' ' ) && ( *c != '/' ) );
306         *c = '\0';
307 
308         if ( DEBUG_BINDSCAN )
309             out_printf( "archive name %s found\n", lar_name );
310 
311         sprintf( buf, "%s(%s)", archive, lar_name );
312 
313         {
314             OBJECT * const member = object_new( buf );
315             timestamp time;
316             timestamp_init( &time, (time_t)lar_date, 0 );
317             (*func)( closure, member, 1 /* time valid */, &time );
318             object_free( member );
319         }
320 
321         offset += SARHDR + ( ( lar_size + 1 ) & ~1 );
322         lseek( fd, offset, 0 );
323     }
324 
325     if ( string_table )
326         BJAM_FREE( string_table );
327 
328     close( fd );
329 #endif  /* NO_AR */
330 }
331 
332 #else  /* AIAMAG - RS6000 AIX */
333 
file_archscan_small(int fd,char const * archive,scanback func,void * closure)334 static void file_archscan_small( int fd, char const * archive, scanback func,
335     void * closure )
336 {
337     struct fl_hdr fl_hdr;
338 
339     struct {
340         struct ar_hdr hdr;
341         char pad[ 256 ];
342     } ar_hdr ;
343 
344     char buf[ MAXJPATH ];
345     long offset;
346 
347     if ( read( fd, (char *)&fl_hdr, FL_HSZ ) != FL_HSZ )
348         return;
349 
350     sscanf( fl_hdr.fl_fstmoff, "%ld", &offset );
351 
352     if ( DEBUG_BINDSCAN )
353         out_printf( "scan archive %s\n", archive );
354 
355     while ( offset > 0 && lseek( fd, offset, 0 ) >= 0 &&
356         read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= (int)sizeof( ar_hdr.hdr ) )
357     {
358         long lar_date;
359         int lar_namlen;
360 
361         sscanf( ar_hdr.hdr.ar_namlen, "%d" , &lar_namlen );
362         sscanf( ar_hdr.hdr.ar_date  , "%ld", &lar_date   );
363         sscanf( ar_hdr.hdr.ar_nxtmem, "%ld", &offset     );
364 
365         if ( !lar_namlen )
366             continue;
367 
368         ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0';
369 
370         sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name );
371 
372         {
373             OBJECT * const member = object_new( buf );
374             timestamp time;
375             timestamp_init( &time, (time_t)lar_date, 0 );
376             (*func)( closure, member, 1 /* time valid */, &time );
377             object_free( member );
378         }
379     }
380 }
381 
382 /* Check for OS versions supporting the big variant. */
383 #ifdef AR_HSZ_BIG
384 
file_archscan_big(int fd,char const * archive,scanback func,void * closure)385 static void file_archscan_big( int fd, char const * archive, scanback func,
386     void * closure )
387 {
388     struct fl_hdr_big fl_hdr;
389 
390     struct {
391         struct ar_hdr_big hdr;
392         char pad[ 256 ];
393     } ar_hdr ;
394 
395     char buf[ MAXJPATH ];
396     long long offset;
397 
398     if ( read( fd, (char *)&fl_hdr, FL_HSZ_BIG ) != FL_HSZ_BIG )
399         return;
400 
401     sscanf( fl_hdr.fl_fstmoff, "%lld", &offset );
402 
403     if ( DEBUG_BINDSCAN )
404         out_printf( "scan archive %s\n", archive );
405 
406     while ( offset > 0 && lseek( fd, offset, 0 ) >= 0 &&
407         read( fd, &ar_hdr, sizeof( ar_hdr ) ) >= sizeof( ar_hdr.hdr ) )
408     {
409         long lar_date;
410         int lar_namlen;
411 
412         sscanf( ar_hdr.hdr.ar_namlen, "%d"  , &lar_namlen );
413         sscanf( ar_hdr.hdr.ar_date  , "%ld" , &lar_date   );
414         sscanf( ar_hdr.hdr.ar_nxtmem, "%lld", &offset     );
415 
416         if ( !lar_namlen )
417             continue;
418 
419         ar_hdr.hdr._ar_name.ar_name[ lar_namlen ] = '\0';
420 
421         sprintf( buf, "%s(%s)", archive, ar_hdr.hdr._ar_name.ar_name );
422 
423         {
424             OBJECT * const member = object_new( buf );
425             timestamp time;
426             timestamp_init( &time, (time_t)lar_date, 0 );
427             (*func)( closure, member, 1 /* time valid */, &time );
428             object_free( member );
429         }
430     }
431 }
432 
433 #endif  /* AR_HSZ_BIG */
434 
file_archscan(char const * archive,scanback func,void * closure)435 void file_archscan( char const * archive, scanback func, void * closure )
436 {
437     int fd;
438     char fl_magic[ SAIAMAG ];
439 
440     if ( ( fd = open( archive, O_RDONLY, 0 ) ) < 0 )
441         return;
442 
443     if ( read( fd, fl_magic, SAIAMAG ) != SAIAMAG ||
444         lseek( fd, 0, SEEK_SET ) == -1 )
445     {
446         close( fd );
447         return;
448     }
449 
450     if ( !strncmp( AIAMAG, fl_magic, SAIAMAG ) )
451     {
452         /* read small variant */
453         file_archscan_small( fd, archive, func, closure );
454     }
455 #ifdef AR_HSZ_BIG
456     else if ( !strncmp( AIAMAGBIG, fl_magic, SAIAMAG ) )
457     {
458         /* read big variant */
459         file_archscan_big( fd, archive, func, closure );
460     }
461 #endif
462 
463     close( fd );
464 }
465 
466 #endif  /* AIAMAG - RS6000 AIX */
467 
468 #endif  /* USE_FILEUNIX */
469