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