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