]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/build/src/engine/filesys.c
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / tools / build / src / engine / filesys.c
CommitLineData
7c673cae
FG
1/*
2 * Copyright 2001-2004 David Abrahams.
3 * Copyright 2005 Rene Rivera.
4 * Distributed under the Boost Software License, Version 1.0.
5 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
6 */
7
8/*
9 * filesys.c - OS independant file system manipulation support
10 *
11 * External routines:
12 * file_build1() - construct a path string based on PATHNAME information
13 * file_dirscan() - scan a directory for files
14 * file_done() - module cleanup called on shutdown
15 * file_info() - return cached information about a path
16 * file_is_file() - return whether a path identifies an existing file
17 * file_query() - get cached information about a path, query the OS if
18 * needed
19 * file_remove_atexit() - schedule a path to be removed on program exit
20 * file_time() - get a file timestamp
21 *
22 * External routines - utilites for OS specific module implementations:
23 * file_query_posix_() - query information about a path using POSIX stat()
24 *
25 * Internal routines:
26 * file_dirscan_impl() - no-profiling worker for file_dirscan()
27 */
28
29
30#include "jam.h"
31#include "filesys.h"
32
33#include "lists.h"
34#include "object.h"
35#include "pathsys.h"
36#include "strings.h"
37#include "output.h"
38
39#include <assert.h>
40#include <sys/stat.h>
41
42
43/* Internal OS specific implementation details - have names ending with an
44 * underscore and are expected to be implemented in an OS specific fileXXX.c
45 * module.
46 */
47void file_dirscan_( file_info_t * const dir, scanback func, void * closure );
48int file_collect_dir_content_( file_info_t * const dir );
49void file_query_( file_info_t * const );
50
51void file_archivescan_( file_archive_info_t * const archive, archive_scanback func,
52 void * closure );
53int file_collect_archive_content_( file_archive_info_t * const archive );
54void file_archive_query_( file_archive_info_t * const );
55
56static void file_archivescan_impl( OBJECT * path, archive_scanback func,
57 void * closure );
58static void file_dirscan_impl( OBJECT * dir, scanback func, void * closure );
59static void free_file_archive_info( void * xarchive, void * data );
60static void free_file_info( void * xfile, void * data );
61
62static void remove_files_atexit( void );
63
64
65static struct hash * filecache_hash;
66static struct hash * archivecache_hash;
67
68
69/*
70 * file_archive_info() - return cached information about an archive
71 *
72 * Returns a default initialized structure containing only queried file's info
73 * in case this is the first time this file system entity has been
74 * referenced.
75 */
76
77file_archive_info_t * file_archive_info( OBJECT * const path, int * found )
78{
79 OBJECT * const path_key = path_as_key( path );
80 file_archive_info_t * archive;
81
82 if ( !archivecache_hash )
83 archivecache_hash = hashinit( sizeof( file_archive_info_t ),
84 "file_archive_info" );
85
86 archive = (file_archive_info_t *)hash_insert( archivecache_hash, path_key,
87 found );
88
89 if ( !*found )
90 {
91 archive->file = 0;
92 archive->members = FL0;
93 }
94 else
95 object_free( path_key );
96
97 return archive;
98}
99
100
101/*
102 * file_archive_query() - get cached information about a archive file path
103 *
104 * Returns 0 in case querying the OS about the given path fails, e.g. because
105 * the path does not reference an existing file system object.
106 */
107
108file_archive_info_t * file_archive_query( OBJECT * const path )
109{
110 int found;
111 file_archive_info_t * const archive = file_archive_info( path, &found );
112 file_info_t * file = file_query( path );
113
114 if ( !( file && file->is_file ) )
115 {
116 return 0;
117 }
118
119 archive->file = file;
120
121
122 return archive;
123}
124
125
126
127/*
128 * file_archivescan() - scan an archive for members
129 */
130
131void file_archivescan( OBJECT * path, archive_scanback func, void * closure )
132{
133 PROFILE_ENTER( FILE_ARCHIVESCAN );
134 file_archivescan_impl( path, func, closure );
135 PROFILE_EXIT( FILE_ARCHIVESCAN );
136}
137
138
139/*
140 * file_build1() - construct a path string based on PATHNAME information
141 */
142
143void file_build1( PATHNAME * const f, string * file )
144{
145 if ( DEBUG_SEARCH )
146 {
147 out_printf( "build file: " );
148 if ( f->f_root.len )
149 out_printf( "root = '%.*s' ", f->f_root.len, f->f_root.ptr );
150 if ( f->f_dir.len )
151 out_printf( "dir = '%.*s' ", f->f_dir.len, f->f_dir.ptr );
152 if ( f->f_base.len )
153 out_printf( "base = '%.*s' ", f->f_base.len, f->f_base.ptr );
154 out_printf( "\n" );
155 }
156
157 /* Start with the grist. If the current grist is not surrounded by <>'s, add
158 * them.
159 */
160 if ( f->f_grist.len )
161 {
162 if ( f->f_grist.ptr[ 0 ] != '<' )
163 string_push_back( file, '<' );
164 string_append_range(
165 file, f->f_grist.ptr, f->f_grist.ptr + f->f_grist.len );
166 if ( file->value[ file->size - 1 ] != '>' )
167 string_push_back( file, '>' );
168 }
169}
170
171
172/*
173 * file_dirscan() - scan a directory for files
174 */
175
176void file_dirscan( OBJECT * dir, scanback func, void * closure )
177{
178 PROFILE_ENTER( FILE_DIRSCAN );
179 file_dirscan_impl( dir, func, closure );
180 PROFILE_EXIT( FILE_DIRSCAN );
181}
182
183
184/*
185 * file_done() - module cleanup called on shutdown
186 */
187
188void file_done()
189{
190 remove_files_atexit();
191 if ( filecache_hash )
192 {
193 hashenumerate( filecache_hash, free_file_info, (void *)0 );
194 hashdone( filecache_hash );
195 }
196
197 if ( archivecache_hash )
198 {
199 hashenumerate( archivecache_hash, free_file_archive_info, (void *)0 );
200 hashdone( archivecache_hash );
201 }
202}
203
204
205/*
206 * file_info() - return cached information about a path
207 *
208 * Returns a default initialized structure containing only the path's normalized
209 * name in case this is the first time this file system entity has been
210 * referenced.
211 */
212
213file_info_t * file_info( OBJECT * const path, int * found )
214{
215 OBJECT * const path_key = path_as_key( path );
216 file_info_t * finfo;
217
218 if ( !filecache_hash )
219 filecache_hash = hashinit( sizeof( file_info_t ), "file_info" );
220
221 finfo = (file_info_t *)hash_insert( filecache_hash, path_key, found );
222 if ( !*found )
223 {
224 finfo->name = path_key;
225 finfo->files = L0;
226 }
227 else
228 object_free( path_key );
229
230 return finfo;
231}
232
233
234/*
235 * file_is_file() - return whether a path identifies an existing file
236 */
237
238int file_is_file( OBJECT * const path )
239{
240 file_info_t const * const ff = file_query( path );
241 return ff ? ff->is_file : -1;
242}
243
244
245/*
246 * file_time() - get a file timestamp
247 */
248
249int file_time( OBJECT * const path, timestamp * const time )
250{
251 file_info_t const * const ff = file_query( path );
252 if ( !ff ) return -1;
253 timestamp_copy( time, &ff->time );
254 return 0;
255}
256
257
258/*
259 * file_query() - get cached information about a path, query the OS if needed
260 *
261 * Returns 0 in case querying the OS about the given path fails, e.g. because
262 * the path does not reference an existing file system object.
263 */
264
265file_info_t * file_query( OBJECT * const path )
266{
267 /* FIXME: Add tracking for disappearing files (i.e. those that can not be
268 * detected by stat() even though they had been detected successfully
269 * before) and see how they should be handled in the rest of Boost Jam code.
270 * Possibly allow Jamfiles to specify some files as 'volatile' which would
271 * make Boost Jam avoid caching information about those files and instead
272 * ask the OS about them every time.
273 */
274 int found;
275 file_info_t * const ff = file_info( path, &found );
276 if ( !found )
277 {
278 file_query_( ff );
279 if ( ff->exists )
280 {
281 /* Set the path's timestamp to 1 in case it is 0 or undetected to avoid
282 * confusion with non-existing paths.
283 */
284 if ( timestamp_empty( &ff->time ) )
285 timestamp_init( &ff->time, 1, 0 );
286 }
287 }
288 if ( !ff->exists )
289 {
290 return 0;
291 }
292 return ff;
293}
294
295
296/*
297 * file_query_posix_() - query information about a path using POSIX stat()
298 *
299 * Fallback file_query_() implementation for OS specific modules.
300 *
301 * Note that the Windows POSIX stat() function implementation suffers from
302 * several issues:
303 * * Does not support file timestamps with resolution finer than 1 second,
304 * meaning it can not be used to detect file timestamp changes of less than
305 * 1 second. One possible consequence is that some fast-paced touch commands
306 * (such as those done by Boost Build's internal testing system if it does
307 * not do some extra waiting) will not be detected correctly by the build
308 * system.
309 * * Returns file modification times automatically adjusted for daylight
310 * savings time even though daylight savings time should have nothing to do
311 * with internal time representation.
312 */
313
314void file_query_posix_( file_info_t * const info )
315{
316 struct stat statbuf;
317 char const * const pathstr = object_str( info->name );
318 char const * const pathspec = *pathstr ? pathstr : ".";
319
320 if ( stat( pathspec, &statbuf ) < 0 )
321 {
322 info->is_file = 0;
323 info->is_dir = 0;
324 info->exists = 0;
325 timestamp_clear( &info->time );
326 }
327 else
328 {
329 info->is_file = statbuf.st_mode & S_IFREG ? 1 : 0;
330 info->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0;
331 info->exists = 1;
332 timestamp_init( &info->time, statbuf.st_mtime, 0 );
333 }
334}
335
336
337/*
338 * file_remove_atexit() - schedule a path to be removed on program exit
339 */
340
341static LIST * files_to_remove = L0;
342
343void file_remove_atexit( OBJECT * const path )
344{
345 files_to_remove = list_push_back( files_to_remove, object_copy( path ) );
346}
347
348
349/*
350 * file_archivescan_impl() - no-profiling worker for file_archivescan()
351 */
352
353static void file_archivescan_impl( OBJECT * path, archive_scanback func, void * closure )
354{
355 file_archive_info_t * const archive = file_archive_query( path );
356 if ( !archive || !archive->file->is_file )
357 return;
358
359 /* Lazy collect the archive content information. */
360 if ( filelist_empty( archive->members ) )
361 {
362 if ( DEBUG_BINDSCAN )
363 printf( "scan archive %s\n", object_str( archive->file->name ) );
364 if ( file_collect_archive_content_( archive ) < 0 )
365 return;
366 }
367
368 /* OS specific part of the file_archivescan operation. */
369 file_archivescan_( archive, func, closure );
370
371 /* Report the collected archive content. */
372 {
373 FILELISTITER iter = filelist_begin( archive->members );
374 FILELISTITER const end = filelist_end( archive->members );
375 char buf[ MAXJPATH ];
376
377 for ( ; iter != end ; iter = filelist_next( iter ) )
378 {
379 file_info_t * member_file = filelist_item( iter );
380 LIST * symbols = member_file->files;
381
382 /* Construct member path: 'archive-path(member-name)'
383 */
384 sprintf( buf, "%s(%s)",
385 object_str( archive->file->name ),
386 object_str( member_file->name ) );
387
388 {
389 OBJECT * const member = object_new( buf );
390 (*func)( closure, member, symbols, 1, &member_file->time );
391 object_free( member );
392 }
393 }
394 }
395}
396
397
398/*
399 * file_dirscan_impl() - no-profiling worker for file_dirscan()
400 */
401
402static void file_dirscan_impl( OBJECT * dir, scanback func, void * closure )
403{
404 file_info_t * const d = file_query( dir );
405 if ( !d || !d->is_dir )
406 return;
407
408 /* Lazy collect the directory content information. */
409 if ( list_empty( d->files ) )
410 {
411 if ( DEBUG_BINDSCAN )
412 out_printf( "scan directory %s\n", object_str( d->name ) );
413 if ( file_collect_dir_content_( d ) < 0 )
414 return;
415 }
416
417 /* OS specific part of the file_dirscan operation. */
418 file_dirscan_( d, func, closure );
419
420 /* Report the collected directory content. */
421 {
422 LISTITER iter = list_begin( d->files );
423 LISTITER const end = list_end( d->files );
424 for ( ; iter != end; iter = list_next( iter ) )
425 {
426 OBJECT * const path = list_item( iter );
427 file_info_t const * const ffq = file_query( path );
428 /* Using a file name read from a file_info_t structure allows OS
429 * specific implementations to store some kind of a normalized file
430 * name there. Using such a normalized file name then allows us to
431 * correctly recognize different file paths actually identifying the
432 * same file. For instance, an implementation may:
433 * - convert all file names internally to lower case on a case
434 * insensitive file system
435 * - convert the NTFS paths to their long path variants as that
436 * file system each file system entity may have a long and a
437 * short path variant thus allowing for many different path
438 * strings identifying the same file.
439 */
440 (*func)( closure, ffq->name, 1 /* stat()'ed */, &ffq->time );
441 }
442 }
443}
444
445
446static void free_file_archive_info( void * xarchive, void * data )
447{
448 file_archive_info_t * const archive = (file_archive_info_t *)xarchive;
449
450 if ( archive ) filelist_free( archive->members );
451}
452
453
454static void free_file_info( void * xfile, void * data )
455{
456 file_info_t * const file = (file_info_t *)xfile;
457 object_free( file->name );
458 list_free( file->files );
459}
460
461
462static void remove_files_atexit( void )
463{
464 LISTITER iter = list_begin( files_to_remove );
465 LISTITER const end = list_end( files_to_remove );
466 for ( ; iter != end; iter = list_next( iter ) )
467 remove( object_str( list_item( iter ) ) );
468 list_free( files_to_remove );
469 files_to_remove = L0;
470}
471
472
473/*
474 * FILELIST linked-list implementation
475 */
476
477FILELIST * filelist_new( OBJECT * path )
478{
479 FILELIST * list = (FILELIST *)BJAM_MALLOC( sizeof( FILELIST ) );
480
481 memset( list, 0, sizeof( *list ) );
482 list->size = 0;
483 list->head = 0;
484 list->tail = 0;
485
486 return filelist_push_back( list, path );
487}
488
489FILELIST * filelist_push_back( FILELIST * list, OBJECT * path )
490{
491 FILEITEM * item;
492 file_info_t * file;
493
494 /* Lazy initialization
495 */
496 if ( filelist_empty( list ) )
497 {
498 list = filelist_new( path );
499 return list;
500 }
501
502
503 item = (FILEITEM *)BJAM_MALLOC( sizeof( FILEITEM ) );
504 memset( item, 0, sizeof( *item ) );
505 item->value = (file_info_t *)BJAM_MALLOC( sizeof( file_info_t ) );
506
507 file = item->value;
508 memset( file, 0, sizeof( *file ) );
509
510 file->name = path;
511 file->files = L0;
512
513 if ( list->tail )
514 {
515 list->tail->next = item;
516 }
517 else
518 {
519 list->head = item;
520 }
521 list->tail = item;
522 list->size++;
523
524 return list;
525}
526
527FILELIST * filelist_push_front( FILELIST * list, OBJECT * path )
528{
529 FILEITEM * item;
530 file_info_t * file;
531
532 /* Lazy initialization
533 */
534 if ( filelist_empty( list ) )
535 {
536 list = filelist_new( path );
537 return list;
538 }
539
540
541 item = (FILEITEM *)BJAM_MALLOC( sizeof( FILEITEM ) );
542 memset( item, 0, sizeof( *item ) );
543 item->value = (file_info_t *)BJAM_MALLOC( sizeof( file_info_t ) );
544
545 file = item->value;
546 memset( file, 0, sizeof( *file ) );
547
548 file->name = path;
549 file->files = L0;
550
551 if ( list->head )
552 {
553 item->next = list->head;
554 }
555 else
556 {
557 list->tail = item;
558 }
559 list->head = item;
560 list->size++;
561
562 return list;
563}
564
565
566FILELIST * filelist_pop_front( FILELIST * list )
567{
568 FILEITEM * item;
569
570 if ( filelist_empty( list ) ) return list;
571
572 item = list->head;
573
574 if ( item )
575 {
576 if ( item->value ) free_file_info( item->value, 0 );
577
578 list->head = item->next;
579 list->size--;
580 if ( !list->size ) list->tail = list->head;
581
582#ifdef BJAM_NO_MEM_CACHE
583 BJAM_FREE( item );
584#endif
585 }
586
587 return list;
588}
589
590int filelist_length( FILELIST * list )
591{
592 int result = 0;
593 if ( !filelist_empty( list ) ) result = list->size;
594
595 return result;
596}
597
598void filelist_free( FILELIST * list )
599{
600 FILELISTITER iter;
601
602 if ( filelist_empty( list ) ) return;
603
604 while ( filelist_length( list ) ) filelist_pop_front( list );
605
606#ifdef BJAM_NO_MEM_CACHE
607 BJAM_FREE( list );
608#endif
609}
610
611int filelist_empty( FILELIST * list )
612{
613 return ( list == FL0 );
614}
615
616
617FILELISTITER filelist_begin( FILELIST * list )
618{
619 if ( filelist_empty( list )
620 || list->head == 0 ) return (FILELISTITER)0;
621
622 return &list->head->value;
623}
624
625
626FILELISTITER filelist_end( FILELIST * list )
627{
628 return (FILELISTITER)0;
629}
630
631
632FILELISTITER filelist_next( FILELISTITER iter )
633{
634 if ( iter )
635 {
636 /* Given FILEITEM.value is defined as first member of FILEITEM structure
637 * and FILELISTITER = &FILEITEM.value,
638 * FILEITEM = *(FILEITEM **)FILELISTITER
639 */
640 FILEITEM * item = (FILEITEM *)iter;
641 iter = ( item->next ? &item->next->value : (FILELISTITER)0 );
642 }
643
644 return iter;
645}
646
647
648file_info_t * filelist_item( FILELISTITER it )
649{
650 file_info_t * result = (file_info_t *)0;
651
652 if ( it )
653 {
654 result = (file_info_t *)*it;
655 }
656
657 return result;
658}
659
660
661file_info_t * filelist_front( FILELIST * list )
662{
663 if ( filelist_empty( list )
664 || list->head == 0 ) return (file_info_t *)0;
665
666 return list->head->value;
667}
668
669
670file_info_t * filelist_back( FILELIST * list )
671{
672 if ( filelist_empty( list )
673 || list->tail == 0 ) return (file_info_t *)0;
674
675 return list->tail->value;
676}