]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
110 | int file_collect_dir_content_( file_info_t * const d ) | |
111 | { | |
112 | LIST * files = L0; | |
113 | PATHNAME f; | |
b32b8144 FG |
114 | int n; |
115 | STRUCT_DIRENT ** namelist; | |
7c673cae FG |
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 | ||
b32b8144 | 132 | if ( -1 == ( n = scandir( dirstr, &namelist, NULL, alphasort ) ) ) |
7c673cae FG |
133 | return -1; |
134 | ||
135 | string_new( path ); | |
b32b8144 | 136 | while ( n-- ) |
7c673cae FG |
137 | { |
138 | OBJECT * name; | |
b32b8144 | 139 | dirent = namelist[ n ]; |
7c673cae FG |
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 ); | |
b32b8144 | 155 | free( dirent ); |
7c673cae FG |
156 | } |
157 | string_free( path ); | |
158 | ||
b32b8144 | 159 | free( namelist ); |
7c673cae FG |
160 | |
161 | d->files = files; | |
162 | return 0; | |
163 | } | |
164 | ||
165 | ||
166 | /* | |
167 | * file_dirscan_() - OS specific file_dirscan() implementation | |
168 | */ | |
169 | ||
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 | ||
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 | ||
198 | void file_query_( file_info_t * const info ) | |
199 | { | |
200 | file_query_posix_( info ); | |
201 | } | |
202 | ||
203 | ||
b32b8144 | 204 | int file_collect_archive_content_( file_archive_info_t * const archive ); |
7c673cae FG |
205 | |
206 | /* | |
207 | * file_archscan() - scan an archive for files | |
208 | */ | |
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 */ |