VirtualBox

source: kBuild/trunk/src/kmk/dir.c@ 3387

Last change on this file since 3387 was 3186, checked in by bird, 6 years ago

kmk: replaced w32ify() as it uses unsafe static buffer and encourages buffer size assumptions.

  • Property svn:eol-style set to native
File size: 43.4 KB
Line 
1/* Directory hashing for GNU Make.
2Copyright (C) 1988-2016 Free Software Foundation, Inc.
3This file is part of GNU Make.
4
5GNU Make is free software; you can redistribute it and/or modify it under the
6terms of the GNU General Public License as published by the Free Software
7Foundation; either version 3 of the License, or (at your option) any later
8version.
9
10GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program. If not, see <http://www.gnu.org/licenses/>. */
16
17#include "makeint.h"
18#include "hash.h"
19#include "filedef.h"
20#include "dep.h"
21
22#ifdef HAVE_DIRENT_H
23# include <dirent.h>
24# define NAMLEN(dirent) strlen((dirent)->d_name)
25# ifdef VMS
26/* its prototype is in vmsdir.h, which is not needed for HAVE_DIRENT_H */
27const char *vmsify (const char *name, int type);
28# endif
29#else
30# define dirent direct
31# define NAMLEN(dirent) (dirent)->d_namlen
32# ifdef HAVE_SYS_NDIR_H
33# include <sys/ndir.h>
34# endif
35# ifdef HAVE_SYS_DIR_H
36# include <sys/dir.h>
37# endif
38# ifdef HAVE_NDIR_H
39# include <ndir.h>
40# endif
41# ifdef HAVE_VMSDIR_H
42# include "vmsdir.h"
43# endif /* HAVE_VMSDIR_H */
44#endif
45/* bird: FreeBSD + smbfs -> readdir() + EBADF */
46#ifdef __FreeBSD__
47# include <sys/mount.h>
48#endif
49/* bird: end */
50
51#ifdef CONFIG_WITH_STRCACHE2
52# include <stddef.h>
53#endif
54
55/* In GNU systems, <dirent.h> defines this macro for us. */
56#ifdef _D_NAMLEN
57# undef NAMLEN
58# define NAMLEN(d) _D_NAMLEN(d)
59#endif
60
61#if (defined (POSIX) || defined (VMS) || defined (WINDOWS32)) && !defined (__GNU_LIBRARY__)
62/* Posix does not require that the d_ino field be present, and some
63 systems do not provide it. */
64# define REAL_DIR_ENTRY(dp) 1
65# define FAKE_DIR_ENTRY(dp)
66#else
67# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
68# define FAKE_DIR_ENTRY(dp) (dp->d_ino = 1)
69#endif /* POSIX */
70
71
72#ifdef __MSDOS__
73#include <ctype.h>
74#include <fcntl.h>
75
76/* If it's MSDOS that doesn't have _USE_LFN, disable LFN support. */
77#ifndef _USE_LFN
78#define _USE_LFN 0
79#endif
80
81static const char *
82dosify (const char *filename)
83{
84 static char dos_filename[14];
85 char *df;
86 int i;
87
88 if (filename == 0 || _USE_LFN)
89 return filename;
90
91 /* FIXME: what about filenames which violate
92 8+3 constraints, like "config.h.in", or ".emacs"? */
93 if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0)
94 return filename;
95
96 df = dos_filename;
97
98 /* First, transform the name part. */
99 for (i = 0; i < 8 && ! STOP_SET (*filename, MAP_DOT|MAP_NUL); ++i)
100 *df++ = tolower ((unsigned char)*filename++);
101
102 /* Now skip to the next dot. */
103 while (! STOP_SET (*filename, MAP_DOT|MAP_NUL))
104 ++filename;
105 if (*filename != '\0')
106 {
107 *df++ = *filename++;
108 for (i = 0; i < 3 && ! STOP_SET (*filename, MAP_DOT|MAP_NUL); ++i)
109 *df++ = tolower ((unsigned char)*filename++);
110 }
111
112 /* Look for more dots. */
113 while (! STOP_SET (*filename, MAP_DOT|MAP_NUL))
114 ++filename;
115 if (*filename == '.')
116 return filename;
117 *df = 0;
118 return dos_filename;
119}
120#endif /* __MSDOS__ */
121
122#ifdef WINDOWS32
123#include <Windows.h>
124#include "pathstuff.h"
125#endif
126
127#ifdef _AMIGA
128#include <ctype.h>
129#endif
130
131#ifdef HAVE_CASE_INSENSITIVE_FS
132static const char *
133downcase (const char *filename)
134{
135 static PATH_VAR (new_filename);
136 char *df;
137
138 if (filename == 0)
139 return 0;
140
141 df = new_filename;
142 while (*filename != '\0')
143 {
144 *df++ = tolower ((unsigned char)*filename);
145 ++filename;
146 }
147
148 *df = 0;
149
150 return new_filename;
151}
152#endif /* HAVE_CASE_INSENSITIVE_FS */
153
154#ifdef VMS
155
156static char *
157downcase_inplace(char *filename)
158{
159 char *name;
160 name = filename;
161 while (*name != '\0')
162 {
163 *name = tolower ((unsigned char)*name);
164 ++name;
165 }
166 return filename;
167}
168
169#ifndef _USE_STD_STAT
170/* VMS 8.2 fixed the VMS stat output to have unique st_dev and st_ino
171 when _USE_STD_STAT is used on the compile line.
172
173 Prior to _USE_STD_STAT support, the st_dev is a pointer to thread
174 static memory containing the device of the last filename looked up.
175
176 Todo: find out if the ino_t still needs to be faked on a directory.
177 */
178
179/* Define this if the older VMS_INO_T is needed */
180#define VMS_INO_T 1
181
182static int
183vms_hash (const char *name)
184{
185 int h = 0;
186
187 while (*name)
188 {
189 unsigned char uc = *name;
190 int g;
191#ifdef HAVE_CASE_INSENSITIVE_FS
192 h = (h << 4) + (isupper (uc) ? tolower (uc) : uc);
193#else
194 h = (h << 4) + uc;
195#endif
196 name++;
197 g = h & 0xf0000000;
198 if (g)
199 {
200 h = h ^ (g >> 24);
201 h = h ^ g;
202 }
203 }
204 return h;
205}
206
207/* fake stat entry for a directory */
208static int
209vmsstat_dir (const char *name, struct stat *st)
210{
211 char *s;
212 int h;
213 DIR *dir;
214
215 dir = opendir (name);
216 if (dir == 0)
217 return -1;
218 closedir (dir);
219 s = strchr (name, ':'); /* find device */
220 if (s)
221 {
222 /* to keep the compiler happy we said "const char *name", now we cheat */
223 *s++ = 0;
224 st->st_dev = (char *)vms_hash (name);
225 h = vms_hash (s);
226 *(s-1) = ':';
227 }
228 else
229 {
230 st->st_dev = 0;
231 h = vms_hash (name);
232 }
233
234 st->st_ino[0] = h & 0xff;
235 st->st_ino[1] = h & 0xff00;
236 st->st_ino[2] = h >> 16;
237
238 return 0;
239}
240
241# define stat(__path, __sbuf) vmsstat_dir (__path, __sbuf)
242
243#endif /* _USE_STD_STAT */
244#endif /* VMS */
245
246
247/* Hash table of directories. */
248
249#ifndef DIRECTORY_BUCKETS
250#ifdef KMK
251# define DIRECTORY_BUCKETS 4096
252# else
253# define DIRECTORY_BUCKETS 199
254# endif
255#endif
256
257struct directory_contents
258 {
259 dev_t dev; /* Device and inode numbers of this dir. */
260#ifdef WINDOWS32
261 /* Inode means nothing on WINDOWS32. Even file key information is
262 * unreliable because it is random per file open and undefined for remote
263 * filesystems. The most unique attribute I can come up with is the fully
264 * qualified name of the directory. Beware though, this is also
265 * unreliable. I'm open to suggestion on a better way to emulate inode. */
266# ifndef CONFIG_WITH_STRCACHE2
267 char *path_key;
268# else
269 char const *path_key; /* strcache'ed */
270# endif
271 time_t ctime;
272 time_t mtime; /* controls check for stale directory cache */
273 int fs_flags; /* FS_FAT, FS_NTFS, ... */
274# define FS_FAT 0x1
275# define FS_NTFS 0x2
276# define FS_UNKNOWN 0x4
277# ifdef KMK
278 time_t last_updated; /**< The last time the directory was re-read. */
279# endif
280#else
281# ifdef VMS_INO_T
282 ino_t ino[3];
283# else
284 ino_t ino;
285# endif
286#endif /* WINDOWS32 */
287 struct hash_table dirfiles; /* Files in this directory. */
288 DIR *dirstream; /* Stream reading this directory. */
289 };
290
291static unsigned long
292directory_contents_hash_1 (const void *key_0)
293{
294 const struct directory_contents *key = key_0;
295 unsigned long hash;
296
297#ifdef WINDOWS32
298# ifndef CONFIG_WITH_STRCACHE2
299 hash = 0;
300 ISTRING_HASH_1 (key->path_key, hash);
301# else /* CONFIG_WITH_STRCACHE2 */
302 hash = strcache2_calc_ptr_hash (&file_strcache, key->path_key);
303# endif /* CONFIG_WITH_STRCACHE2 */
304 hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) key->ctime;
305#else
306# ifdef VMS_INO_T
307 hash = (((unsigned int) key->dev << 4)
308 ^ ((unsigned int) key->ino[0]
309 + (unsigned int) key->ino[1]
310 + (unsigned int) key->ino[2]));
311# else
312 hash = ((unsigned int) key->dev << 4) ^ (unsigned int) key->ino;
313# endif
314#endif /* WINDOWS32 */
315 return hash;
316}
317
318static unsigned long
319directory_contents_hash_2 (const void *key_0)
320{
321 const struct directory_contents *key = key_0;
322 unsigned long hash;
323
324#ifdef WINDOWS32
325# ifndef CONFIG_WITH_STRCACHE2
326 hash = 0;
327 ISTRING_HASH_2 (key->path_key, hash);
328# else /* CONFIG_WITH_STRCACHE2 */
329 hash = strcache2_get_hash (&file_strcache, key->path_key);
330# endif /* CONFIG_WITH_STRCACHE2 */
331 hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ctime;
332#else
333# ifdef VMS_INO_T
334 hash = (((unsigned int) key->dev << 4)
335 ^ ~((unsigned int) key->ino[0]
336 + (unsigned int) key->ino[1]
337 + (unsigned int) key->ino[2]));
338# else
339 hash = ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ino;
340# endif
341#endif /* WINDOWS32 */
342
343 return hash;
344}
345
346/* Sometimes it's OK to use subtraction to get this value:
347 result = X - Y;
348 But, if we're not sure of the type of X and Y they may be too large for an
349 int (on a 64-bit system for example). So, use ?: instead.
350 See Savannah bug #15534.
351
352 NOTE! This macro has side-effects!
353*/
354
355#define MAKECMP(_x,_y) ((_x)<(_y)?-1:((_x)==(_y)?0:1))
356
357static int
358directory_contents_hash_cmp (const void *xv, const void *yv)
359{
360 const struct directory_contents *x = xv;
361 const struct directory_contents *y = yv;
362 int result;
363
364#ifdef WINDOWS32
365# ifndef CONFIG_WITH_STRCACHE2
366 ISTRING_COMPARE (x->path_key, y->path_key, result);
367 if (result)
368 return result;
369# else /* CONFIG_WITH_STRCACHE2 */
370 if (x->path_key != y->path_key)
371 return -1;
372# endif /* CONFIG_WITH_STRCACHE2 */
373 result = MAKECMP(x->ctime, y->ctime);
374 if (result)
375 return result;
376#else
377# ifdef VMS_INO_T
378 result = MAKECMP(x->ino[0], y->ino[0]);
379 if (result)
380 return result;
381 result = MAKECMP(x->ino[1], y->ino[1]);
382 if (result)
383 return result;
384 result = MAKECMP(x->ino[2], y->ino[2]);
385 if (result)
386 return result;
387# else
388 result = MAKECMP(x->ino, y->ino);
389 if (result)
390 return result;
391# endif
392#endif /* WINDOWS32 */
393
394 return MAKECMP(x->dev, y->dev);
395}
396
397/* Table of directory contents hashed by device and inode number. */
398static struct hash_table directory_contents;
399
400#ifdef CONFIG_WITH_ALLOC_CACHES
401/* Allocation cache for directory contents. */
402struct alloccache directory_contents_cache;
403#endif
404
405struct directory
406 {
407 const char *name; /* Name of the directory. */
408
409 /* The directory's contents. This data may be shared by several
410 entries in the hash table, which refer to the same directory
411 (identified uniquely by 'dev' and 'ino') under different names. */
412 struct directory_contents *contents;
413 };
414
415#ifndef CONFIG_WITH_STRCACHE2
416static unsigned long
417directory_hash_1 (const void *key)
418{
419 return_ISTRING_HASH_1 (((const struct directory *) key)->name);
420}
421
422static unsigned long
423directory_hash_2 (const void *key)
424{
425 return_ISTRING_HASH_2 (((const struct directory *) key)->name);
426}
427
428static int
429directory_hash_cmp (const void *x, const void *y)
430{
431 return_ISTRING_COMPARE (((const struct directory *) x)->name,
432 ((const struct directory *) y)->name);
433}
434#endif /* !CONFIG_WITH_STRCACHE2 */
435
436/* Table of directories hashed by name. */
437static struct hash_table directories;
438
439#ifdef CONFIG_WITH_ALLOC_CACHES
440/* Allocation cache for directories. */
441struct alloccache directories_cache;
442#endif
443
444/* Never have more than this many directories open at once. */
445
446#define MAX_OPEN_DIRECTORIES 10
447
448static unsigned int open_directories = 0;
449
450
451/* Hash table of files in each directory. */
452
453struct dirfile
454 {
455 const char *name; /* Name of the file. */
456 size_t length;
457 short impossible; /* This file is impossible. */
458 };
459
460#ifndef CONFIG_WITH_STRCACHE2
461static unsigned long
462dirfile_hash_1 (const void *key)
463{
464 return_ISTRING_HASH_1 (((struct dirfile const *) key)->name);
465}
466
467static unsigned long
468dirfile_hash_2 (const void *key)
469{
470 return_ISTRING_HASH_2 (((struct dirfile const *) key)->name);
471}
472
473static int
474dirfile_hash_cmp (const void *xv, const void *yv)
475{
476 const struct dirfile *x = xv;
477 const struct dirfile *y = yv;
478 int result = x->length - y->length;
479 if (result)
480 return result;
481 return_ISTRING_COMPARE (x->name, y->name);
482}
483#endif /* !CONFIG_WITH_STRCACHE2 */
484
485#ifndef DIRFILE_BUCKETS
486#define DIRFILE_BUCKETS 107
487#endif
488
489#ifdef CONFIG_WITH_ALLOC_CACHES
490/* Allocation cache for dirfiles. */
491struct alloccache dirfile_cache;
492#endif
493
494
495
496static int dir_contents_file_exists_p (struct directory_contents *dir,
497 const char *filename);
498static struct directory *find_directory (const char *name);
499
500/* Find the directory named NAME and return its 'struct directory'. */
501
502static struct directory *
503find_directory (const char *name)
504{
505 struct directory *dir;
506 struct directory **dir_slot;
507 struct directory dir_key;
508
509#ifndef CONFIG_WITH_STRCACHE2
510 dir_key.name = name;
511 dir_slot = (struct directory **) hash_find_slot (&directories, &dir_key);
512#else
513 const char *p = name + strlen (name);
514# if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
515 dir_key.name = strcache_add_len (downcase(name), p - name);
516# else
517 dir_key.name = strcache_add_len (name, p - name);
518# endif
519 dir_slot = (struct directory **) hash_find_slot_strcached (&directories, &dir_key);
520#endif
521 dir = *dir_slot;
522
523 if (HASH_VACANT (dir))
524 {
525 /* The directory was not found. Create a new entry for it. */
526#ifndef CONFIG_WITH_STRCACHE2
527 const char *p = name + strlen (name);
528#endif
529 struct stat st;
530 int r;
531
532#ifndef CONFIG_WITH_ALLOC_CACHES
533 dir = xmalloc (sizeof (struct directory));
534#else
535 dir = alloccache_alloc (&directories_cache);
536#endif
537#ifndef CONFIG_WITH_STRCACHE2
538#if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
539 /* Todo: Why is this only needed on VMS? */
540 {
541 char *lname = downcase_inplace (xstrdup (name));
542 dir->name = strcache_add_len (lname, p - name);
543 free (lname);
544 }
545#else
546 dir->name = strcache_add_len (name, p - name);
547#endif
548#else /* CONFIG_WITH_STRCACHE2 */
549 dir->name = dir_key.name;
550#endif /* CONFIG_WITH_STRCACHE2 */
551 hash_insert_at (&directories, dir, dir_slot);
552 /* The directory is not in the name hash table.
553 Find its device and inode numbers, and look it up by them. */
554
555#if defined(WINDOWS32)
556 {
557 char tem[MAXPATHLEN], *tstart, *tend;
558
559 /* Remove any trailing slashes. Windows32 stat fails even on
560 valid directories if they end in a slash. */
561 memcpy (tem, name, p - name + 1);
562 tstart = tem;
563 if (tstart[1] == ':')
564 tstart += 2;
565 for (tend = tem + (p - name - 1);
566 tend > tstart && (*tend == '/' || *tend == '\\');
567 tend--)
568 *tend = '\0';
569
570 r = stat (tem, &st);
571 }
572#else
573 EINTRLOOP (r, stat (name, &st));
574#endif
575
576 if (r < 0)
577 {
578 /* Couldn't stat the directory. Mark this by
579 setting the 'contents' member to a nil pointer. */
580 dir->contents = 0;
581 }
582 else
583 {
584 /* Search the contents hash table; device and inode are the key. */
585
586#ifdef WINDOWS32
587 PATH_VAR (w32_fullpath);
588 char *w32_path;
589#endif
590 struct directory_contents *dc;
591 struct directory_contents **dc_slot;
592 struct directory_contents dc_key;
593
594 dc_key.dev = st.st_dev;
595#ifdef WINDOWS32
596 w32_path = unix_slashes_resolved (name, w32_fullpath, GET_PATH_MAX);
597# ifndef CONFIG_WITH_STRCACHE2
598 dc_key.path_key = w32_path; /* = w32ify (name, 1); - bird */
599# else /* CONFIG_WITH_STRCACHE2 */
600 dc_key.path_key = strcache_add (w32_path);
601# endif /* CONFIG_WITH_STRCACHE2 */
602 dc_key.ctime = st.st_ctime;
603#else
604# ifdef VMS_INO_T
605 dc_key.ino[0] = st.st_ino[0];
606 dc_key.ino[1] = st.st_ino[1];
607 dc_key.ino[2] = st.st_ino[2];
608# else
609 dc_key.ino = st.st_ino;
610# endif
611#endif
612 dc_slot = (struct directory_contents **) hash_find_slot (&directory_contents, &dc_key);
613 dc = *dc_slot;
614
615 if (HASH_VACANT (dc))
616 {
617 /* Nope; this really is a directory we haven't seen before. */
618#ifdef WINDOWS32
619 char fs_label[BUFSIZ];
620 char fs_type[BUFSIZ];
621 unsigned long fs_serno;
622 unsigned long fs_flags;
623 unsigned long fs_len;
624#endif
625#if defined(WINDOWS32) && defined(KMK)
626 static char s_last_volume[4];
627 static int s_last_flags;
628#endif
629
630#ifndef CONFIG_WITH_ALLOC_CACHES
631 dc = (struct directory_contents *)
632 xmalloc (sizeof (struct directory_contents));
633#else
634 dc = (struct directory_contents *)
635 alloccache_alloc (&directory_contents_cache);
636#endif
637
638 /* Enter it in the contents hash table. */
639 dc->dev = st.st_dev;
640#ifdef WINDOWS32
641# ifndef CONFIG_WITH_STRCACHE2
642 dc->path_key = xstrdup (w32_path);
643# else /* CONFIG_WITH_STRCACHE2 */
644 dc->path_key = dc_key.path_key;
645# endif /* CONFIG_WITH_STRCACHE2 */
646
647 dc->ctime = st.st_ctime;
648 dc->mtime = st.st_mtime;
649# ifdef KMK
650 dc->last_updated = time(NULL);
651# endif
652
653 /* NTFS is the only WINDOWS32 filesystem that bumps mtime on a
654 directory when files are added/deleted from a directory. */
655 w32_path[3] = '\0';
656
657# ifdef KMK /* Need for speed: Cache the GetVolumeInformation result. */
658 if ( s_last_volume[0] == w32_path[0]
659 && s_last_volume[1] == w32_path[1]
660 && s_last_volume[2] == w32_path[2]
661 && s_last_volume[3] == w32_path[3])
662 dc->fs_flags = s_last_flags;
663 else
664 {
665# endif
666 if (GetVolumeInformation (w32_path, fs_label, sizeof (fs_label),
667 &fs_serno, &fs_len, &fs_flags, fs_type,
668 sizeof (fs_type)) == FALSE)
669 dc->fs_flags = FS_UNKNOWN;
670 else if (!strcmp (fs_type, "FAT"))
671 dc->fs_flags = FS_FAT;
672 else if (!strcmp (fs_type, "NTFS"))
673 dc->fs_flags = FS_NTFS;
674 else
675 dc->fs_flags = FS_UNKNOWN;
676# ifdef KMK
677 s_last_volume[0] = w32_path[0];
678 s_last_volume[1] = w32_path[1];
679 s_last_volume[2] = w32_path[2];
680 s_last_volume[3] = w32_path[3];
681 s_last_flags = dc->fs_flags;
682# endif
683#else
684# ifdef VMS_INO_T
685 dc->ino[0] = st.st_ino[0];
686 dc->ino[1] = st.st_ino[1];
687 dc->ino[2] = st.st_ino[2];
688# else
689 dc->ino = st.st_ino;
690# endif
691#endif /* WINDOWS32 */
692 hash_insert_at (&directory_contents, dc, dc_slot);
693 ENULLLOOP (dc->dirstream, opendir (name));
694 if (dc->dirstream == 0)
695 /* Couldn't open the directory. Mark this by setting the
696 'files' member to a nil pointer. */
697 dc->dirfiles.ht_vec = 0;
698 else
699 {
700#ifdef KMK
701 int buckets = st.st_nlink * 2;
702 if (buckets < DIRFILE_BUCKETS)
703 buckets = DIRFILE_BUCKETS;
704 hash_init_strcached (&dc->dirfiles, buckets, &file_strcache,
705 offsetof (struct dirfile, name));
706#else
707# ifndef CONFIG_WITH_STRCACHE2
708 hash_init (&dc->dirfiles, DIRFILE_BUCKETS,
709 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
710# else /* CONFIG_WITH_STRCACHE2 */
711 hash_init_strcached (&dc->dirfiles, DIRFILE_BUCKETS,
712 &file_strcache,
713 offsetof (struct dirfile, name));
714# endif /* CONFIG_WITH_STRCACHE2 */
715#endif
716 ++open_directories;
717 if (open_directories == MAX_OPEN_DIRECTORIES)
718 /* We have too many directories open already.
719 Read the entire directory and then close it. */
720 dir_contents_file_exists_p (dc, 0);
721 }
722 }
723
724 /* Point the name-hashed entry for DIR at its contents data. */
725 dir->contents = dc;
726 }
727 }
728
729 return dir;
730}
731
732
733/* Return 1 if the name FILENAME is entered in DIR's hash table.
734 FILENAME must contain no slashes. */
735
736static int
737dir_contents_file_exists_p (struct directory_contents *dir,
738 const char *filename)
739{
740 struct dirfile *df;
741 struct dirent *d;
742#ifdef WINDOWS32
743# ifndef KMK
744 struct stat st;
745# endif
746 int rehash = 0;
747#endif
748#ifdef KMK
749 int ret = 0;
750#endif
751
752 if (dir == 0 || dir->dirfiles.ht_vec == 0)
753 /* The directory could not be stat'd or opened. */
754 return 0;
755
756#ifdef __MSDOS__
757 filename = dosify (filename);
758#endif
759
760#ifdef HAVE_CASE_INSENSITIVE_FS
761 filename = downcase (filename);
762#endif
763
764#ifdef __EMX__
765 if (filename != 0)
766 _fnlwr (filename); /* lower case for FAT drives */
767#endif
768 if (filename != 0)
769 {
770 struct dirfile dirfile_key;
771
772 if (*filename == '\0')
773 {
774 /* Checking if the directory exists. */
775 return 1;
776 }
777#ifndef CONFIG_WITH_STRCACHE2
778 dirfile_key.name = filename;
779 dirfile_key.length = strlen (filename);
780 df = hash_find_item (&dir->dirfiles, &dirfile_key);
781#else /* CONFIG_WITH_STRCACHE2 */
782 dirfile_key.length = strlen (filename);
783 dirfile_key.name = filename
784 = strcache_add_len (filename, dirfile_key.length);
785 df = hash_find_item_strcached (&dir->dirfiles, &dirfile_key);
786#endif /* CONFIG_WITH_STRCACHE2 */
787 if (df)
788 return !df->impossible;
789 }
790
791 /* The file was not found in the hashed list.
792 Try to read the directory further. */
793
794 if (dir->dirstream == 0)
795 {
796#if defined(WINDOWS32) && !defined(KMK)
797 /*
798 * Check to see if directory has changed since last read. FAT
799 * filesystems force a rehash always as mtime does not change
800 * on directories (ugh!).
801 */
802# ifdef KMK
803 if (dir->path_key && time(NULL) > dc->last_updated + 2) /* KMK: Only recheck every 2 seconds. */
804# else
805 if (dir->path_key)
806# endif
807 {
808 if ((dir->fs_flags & FS_FAT) != 0)
809 {
810 dir->mtime = time ((time_t *) 0);
811 rehash = 1;
812 }
813# ifdef KMK
814 else if ( birdStatModTimeOnly (dir->path_key, &st.st_mtim, 1) == 0
815 && st.st_mtime > dir->mtime)
816# else
817 else if (stat (dir->path_key, &st) == 0 && st.st_mtime > dir->mtime)
818# endif
819 {
820 /* reset date stamp to show most recent re-process. */
821 dir->mtime = st.st_mtime;
822 rehash = 1;
823 }
824
825
826 /* If it has been already read in, all done. */
827 if (!rehash)
828 return 0;
829
830 /* make sure directory can still be opened; if not return. */
831 dir->dirstream = opendir (dir->path_key);
832 if (!dir->dirstream)
833 return 0;
834# ifdef KMK
835 dc->last_updated = time(NULL);
836# endif
837 }
838 else
839#endif
840 /* The directory has been all read in. */
841 return 0;
842 }
843
844 while (1)
845 {
846 /* Enter the file in the hash table. */
847 unsigned int len;
848 struct dirfile dirfile_key;
849 struct dirfile **dirfile_slot;
850
851 ENULLLOOP (d, readdir (dir->dirstream));
852 if (d == 0)
853 {
854/* bird: Workaround for smbfs mounts returning EBADF at the end of the search.
855 To exactly determin the cause here, I should probably do some smbfs
856 tracing, but for now just ignoring the EBADF on seems to work.
857 (The smb server is 64-bit vista, btw.) */
858#if defined (__FreeBSD__)
859 struct statfs stfs;
860 int saved_errno = errno;
861 errno = 0;
862 if (saved_errno == EBADF
863 && !fstatfs (dirfd (dir->dirstream), &stfs)
864 && !(stfs.f_flags & MNT_LOCAL)
865 && !strcmp(stfs.f_fstypename, "smbfs"))
866 {
867 /*fprintf (stderr, "EBADF on remote fs! dirfd=%d errno=%d\n",
868 dirfd (dir->dirstream), errno);*/
869 saved_errno = 0;
870 }
871 errno = saved_errno;
872#endif
873/* bird: end */
874 if (errno)
875 pfatal_with_name ("INTERNAL: readdir");
876 break;
877 }
878
879#if defined(VMS) && defined(HAVE_DIRENT_H)
880 /* In VMS we get file versions too, which have to be stripped off.
881 Some versions of VMS return versions on Unix files even when
882 the feature option to strip them is set. */
883 {
884 char *p = strrchr (d->d_name, ';');
885 if (p)
886 *p = '\0';
887 }
888#endif
889 if (!REAL_DIR_ENTRY (d))
890 continue;
891
892 len = NAMLEN (d);
893#ifndef CONFIG_WITH_STRCACHE2
894 dirfile_key.name = d->d_name;
895 dirfile_key.length = len;
896 dirfile_slot = (struct dirfile **) hash_find_slot (&dir->dirfiles, &dirfile_key);
897#else
898# if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
899 dirfile_key.name = strcache_add_len (downcase(d->d_name), len);
900# else
901 dirfile_key.name = strcache_add_len (d->d_name, len);
902# endif
903 dirfile_key.length = len;
904 dirfile_slot = (struct dirfile **) hash_find_slot_strcached (&dir->dirfiles, &dirfile_key);
905#endif
906#ifdef WINDOWS32
907 /*
908 * If re-reading a directory, don't cache files that have
909 * already been discovered.
910 */
911 if (! rehash || HASH_VACANT (*dirfile_slot))
912#endif
913 {
914#ifndef CONFIG_WITH_ALLOC_CACHES
915 df = xmalloc (sizeof (struct dirfile));
916#else
917 df = alloccache_alloc (&dirfile_cache);
918#endif
919#ifndef CONFIG_WITH_STRCACHE2
920#if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
921 /* TODO: Why is this only needed on VMS? */
922 df->name = strcache_add_len (downcase_inplace (d->d_name), len);
923#else
924 df->name = strcache_add_len (d->d_name, len);
925#endif
926#else /* CONFIG_WITH_STRCACHE2 */
927 df->name = dirfile_key.name;
928#endif /* CONFIG_WITH_STRCACHE2 */
929 df->length = len;
930 df->impossible = 0;
931 hash_insert_at (&dir->dirfiles, df, dirfile_slot);
932 }
933 /* Check if the name matches the one we're searching for. */
934#ifndef CONFIG_WITH_STRCACHE2
935 if (filename != 0 && patheq (d->d_name, filename))
936#else
937 if (filename != 0 && dirfile_key.name == filename)
938#endif
939#ifdef KMK
940 ret = 1; /* Cache the whole dir. Prevents trouble on windows and os2 during 'rebuild'. */
941#else
942 return 1;
943#endif
944 }
945
946 /* If the directory has been completely read in,
947 close the stream and reset the pointer to nil. */
948 if (d == 0)
949 {
950 --open_directories;
951 closedir (dir->dirstream);
952 dir->dirstream = 0;
953 }
954#ifdef KMK
955 return ret;
956#else
957 return 0;
958#endif
959}
960
961/* Return 1 if the name FILENAME in directory DIRNAME
962 is entered in the dir hash table.
963 FILENAME must contain no slashes. */
964
965int
966dir_file_exists_p (const char *dirname, const char *filename)
967{
968#ifdef VMS
969 if ((filename != NULL) && (dirname != NULL))
970 {
971 int want_vmsify;
972 want_vmsify = (strpbrk (dirname, ":<[") != NULL);
973 if (want_vmsify)
974 filename = vmsify (filename, 0);
975 }
976#endif
977 return dir_contents_file_exists_p (find_directory (dirname)->contents,
978 filename);
979}
980
981
982/* Return 1 if the file named NAME exists. */
983
984int
985file_exists_p (const char *name)
986{
987 const char *dirend;
988 const char *dirname;
989 const char *slash;
990
991#ifndef NO_ARCHIVES
992 if (ar_name (name))
993 return ar_member_date (name) != (time_t) -1;
994#endif
995
996 dirend = strrchr (name, '/');
997#ifdef VMS
998 if (dirend == 0)
999 {
1000 dirend = strrchr (name, ']');
1001 dirend == NULL ? dirend : dirend++;
1002 }
1003 if (dirend == 0)
1004 {
1005 dirend = strrchr (name, '>');
1006 dirend == NULL ? dirend : dirend++;
1007 }
1008 if (dirend == 0)
1009 {
1010 dirend = strrchr (name, ':');
1011 dirend == NULL ? dirend : dirend++;
1012 }
1013#endif /* VMS */
1014#ifdef HAVE_DOS_PATHS
1015 /* Forward and backslashes might be mixed. We need the rightmost one. */
1016 {
1017 const char *bslash = strrchr (name, '\\');
1018 if (!dirend || bslash > dirend)
1019 dirend = bslash;
1020 /* The case of "d:file". */
1021 if (!dirend && name[0] && name[1] == ':')
1022 dirend = name + 1;
1023 }
1024#endif /* HAVE_DOS_PATHS */
1025 if (dirend == 0)
1026#ifndef _AMIGA
1027 return dir_file_exists_p (".", name);
1028#else /* !AMIGA */
1029 return dir_file_exists_p ("", name);
1030#endif /* AMIGA */
1031
1032 slash = dirend;
1033 if (dirend == name)
1034 dirname = "/";
1035 else
1036 {
1037 char *p;
1038#ifdef HAVE_DOS_PATHS
1039 /* d:/ and d: are *very* different... */
1040 if (dirend < name + 3 && name[1] == ':' &&
1041 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
1042 dirend++;
1043#endif
1044 p = alloca (dirend - name + 1);
1045 memcpy (p, name, dirend - name);
1046 p[dirend - name] = '\0';
1047 dirname = p;
1048 }
1049#ifdef VMS
1050 if (*slash == '/')
1051 slash++;
1052#else
1053 slash++;
1054#endif
1055 return dir_file_exists_p (dirname, slash);
1056}
1057
1058
1059/* Mark FILENAME as 'impossible' for 'file_impossible_p'.
1060 This means an attempt has been made to search for FILENAME
1061 as an intermediate file, and it has failed. */
1062
1063void
1064file_impossible (const char *filename)
1065{
1066 const char *dirend;
1067 const char *p = filename;
1068 struct directory *dir;
1069 struct dirfile *new;
1070
1071 dirend = strrchr (p, '/');
1072#ifdef VMS
1073 if (dirend == NULL)
1074 {
1075 dirend = strrchr (p, ']');
1076 dirend == NULL ? dirend : dirend++;
1077 }
1078 if (dirend == NULL)
1079 {
1080 dirend = strrchr (p, '>');
1081 dirend == NULL ? dirend : dirend++;
1082 }
1083 if (dirend == NULL)
1084 {
1085 dirend = strrchr (p, ':');
1086 dirend == NULL ? dirend : dirend++;
1087 }
1088#endif
1089#ifdef HAVE_DOS_PATHS
1090 /* Forward and backslashes might be mixed. We need the rightmost one. */
1091 {
1092 const char *bslash = strrchr (p, '\\');
1093 if (!dirend || bslash > dirend)
1094 dirend = bslash;
1095 /* The case of "d:file". */
1096 if (!dirend && p[0] && p[1] == ':')
1097 dirend = p + 1;
1098 }
1099#endif /* HAVE_DOS_PATHS */
1100 if (dirend == 0)
1101#ifdef _AMIGA
1102 dir = find_directory ("");
1103#else /* !AMIGA */
1104 dir = find_directory (".");
1105#endif /* AMIGA */
1106 else
1107 {
1108 const char *dirname;
1109 const char *slash = dirend;
1110 if (dirend == p)
1111 dirname = "/";
1112 else
1113 {
1114 char *cp;
1115#ifdef HAVE_DOS_PATHS
1116 /* d:/ and d: are *very* different... */
1117 if (dirend < p + 3 && p[1] == ':' &&
1118 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
1119 dirend++;
1120#endif
1121 cp = alloca (dirend - p + 1);
1122 memcpy (cp, p, dirend - p);
1123 cp[dirend - p] = '\0';
1124 dirname = cp;
1125 }
1126 dir = find_directory (dirname);
1127#ifdef VMS
1128 if (*slash == '/')
1129 filename = p = slash + 1;
1130 else
1131 filename = p = slash;
1132#else
1133 filename = p = slash + 1;
1134#endif
1135 }
1136
1137 if (dir->contents == 0)
1138 /* The directory could not be stat'd. We allocate a contents
1139 structure for it, but leave it out of the contents hash table. */
1140#ifndef CONFIG_WITH_ALLOC_CACHES
1141 dir->contents = xcalloc (sizeof (struct directory_contents));
1142#else
1143 dir->contents = alloccache_calloc (&directory_contents_cache);
1144#endif
1145
1146 if (dir->contents->dirfiles.ht_vec == 0)
1147 {
1148#ifndef CONFIG_WITH_STRCACHE2
1149 hash_init (&dir->contents->dirfiles, DIRFILE_BUCKETS,
1150 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
1151#else /* CONFIG_WITH_STRCACHE2 */
1152 hash_init_strcached (&dir->contents->dirfiles, DIRFILE_BUCKETS,
1153 &file_strcache, offsetof (struct dirfile, name));
1154#endif /* CONFIG_WITH_STRCACHE2 */
1155 }
1156
1157 /* Make a new entry and put it in the table. */
1158
1159#ifndef CONFIG_WITH_ALLOC_CACHES
1160 new = xmalloc (sizeof (struct dirfile));
1161#else
1162 new = alloccache_alloc (&dirfile_cache);
1163#endif
1164 new->length = strlen (filename);
1165#if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
1166 /* todo: Why is this only needed on VMS? */
1167 new->name = strcache_add_len (downcase (filename), new->length);
1168#else
1169 new->name = strcache_add_len (filename, new->length);
1170#endif
1171 new->impossible = 1;
1172#ifndef CONFIG_WITH_STRCACHE2
1173 hash_insert (&dir->contents->dirfiles, new);
1174#else /* CONFIG_WITH_STRCACHE2 */
1175 hash_insert_strcached (&dir->contents->dirfiles, new);
1176#endif /* CONFIG_WITH_STRCACHE2 */
1177}
1178
1179
1180/* Return nonzero if FILENAME has been marked impossible. */
1181
1182int
1183file_impossible_p (const char *filename)
1184{
1185 const char *dirend;
1186 struct directory_contents *dir;
1187 struct dirfile *dirfile;
1188 struct dirfile dirfile_key;
1189#ifdef VMS
1190 int want_vmsify = 0;
1191#endif
1192
1193 dirend = strrchr (filename, '/');
1194#ifdef VMS
1195 if (dirend == NULL)
1196 {
1197 want_vmsify = (strpbrk (filename, "]>:^") != NULL);
1198 dirend = strrchr (filename, ']');
1199 }
1200 if (dirend == NULL && want_vmsify)
1201 dirend = strrchr (filename, '>');
1202 if (dirend == NULL && want_vmsify)
1203 dirend = strrchr (filename, ':');
1204#endif
1205#ifdef HAVE_DOS_PATHS
1206 /* Forward and backslashes might be mixed. We need the rightmost one. */
1207 {
1208 const char *bslash = strrchr (filename, '\\');
1209 if (!dirend || bslash > dirend)
1210 dirend = bslash;
1211 /* The case of "d:file". */
1212 if (!dirend && filename[0] && filename[1] == ':')
1213 dirend = filename + 1;
1214 }
1215#endif /* HAVE_DOS_PATHS */
1216 if (dirend == 0)
1217#ifdef _AMIGA
1218 dir = find_directory ("")->contents;
1219#else /* !AMIGA */
1220 dir = find_directory (".")->contents;
1221#endif /* AMIGA */
1222 else
1223 {
1224 const char *dirname;
1225 const char *slash = dirend;
1226 if (dirend == filename)
1227 dirname = "/";
1228 else
1229 {
1230 char *cp;
1231#ifdef HAVE_DOS_PATHS
1232 /* d:/ and d: are *very* different... */
1233 if (dirend < filename + 3 && filename[1] == ':' &&
1234 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
1235 dirend++;
1236#endif
1237 cp = alloca (dirend - filename + 1);
1238 memcpy (cp, filename, dirend - filename);
1239 cp[dirend - filename] = '\0';
1240 dirname = cp;
1241 }
1242 dir = find_directory (dirname)->contents;
1243#ifdef VMS
1244 if (*slash == '/')
1245 filename = slash + 1;
1246 else
1247 filename = slash;
1248#else
1249 filename = slash + 1;
1250#endif
1251 }
1252
1253 if (dir == 0 || dir->dirfiles.ht_vec == 0)
1254 /* There are no files entered for this directory. */
1255 return 0;
1256
1257#ifdef __MSDOS__
1258 filename = dosify (filename);
1259#endif
1260#ifdef HAVE_CASE_INSENSITIVE_FS
1261 filename = downcase (filename);
1262#endif
1263#ifdef VMS
1264 if (want_vmsify)
1265 filename = vmsify (filename, 1);
1266#endif
1267
1268#ifndef CONFIG_WITH_STRCACHE2
1269 dirfile_key.name = filename;
1270 dirfile_key.length = strlen (filename);
1271 dirfile = hash_find_item (&dir->dirfiles, &dirfile_key);
1272#else
1273 dirfile_key.length = strlen (filename);
1274 dirfile_key.name = strcache_add_len (filename, dirfile_key.length);
1275 dirfile = hash_find_item_strcached (&dir->dirfiles, &dirfile_key);
1276#endif
1277 if (dirfile)
1278 return dirfile->impossible;
1279
1280 return 0;
1281}
1282
1283
1284/* Return the already allocated name in the
1285 directory hash table that matches DIR. */
1286
1287const char *
1288dir_name (const char *dir)
1289{
1290 return find_directory (dir)->name;
1291}
1292
1293
1294/* Print the data base of directories. */
1295
1296void
1297print_dir_data_base (void)
1298{
1299 unsigned int files;
1300 unsigned int impossible;
1301 struct directory **dir_slot;
1302 struct directory **dir_end;
1303
1304 puts (_("\n# Directories\n"));
1305
1306 files = impossible = 0;
1307
1308 dir_slot = (struct directory **) directories.ht_vec;
1309 dir_end = dir_slot + directories.ht_size;
1310 for ( ; dir_slot < dir_end; dir_slot++)
1311 {
1312 struct directory *dir = *dir_slot;
1313 if (! HASH_VACANT (dir))
1314 {
1315 if (dir->contents == 0)
1316 printf (_("# %s: could not be stat'd.\n"), dir->name);
1317 else if (dir->contents->dirfiles.ht_vec == 0)
1318 {
1319#ifdef WINDOWS32
1320 printf (_("# %s (key %s, mtime %I64u): could not be opened.\n"),
1321 dir->name, dir->contents->path_key,
1322 (unsigned long long)dir->contents->mtime);
1323#else /* WINDOWS32 */
1324#ifdef VMS_INO_T
1325 printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
1326 dir->name, dir->contents->dev,
1327 dir->contents->ino[0], dir->contents->ino[1],
1328 dir->contents->ino[2]);
1329#else
1330 printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
1331 dir->name, (long int) dir->contents->dev,
1332 (long int) dir->contents->ino);
1333#endif
1334#endif /* WINDOWS32 */
1335 }
1336 else
1337 {
1338 unsigned int f = 0;
1339 unsigned int im = 0;
1340 struct dirfile **files_slot;
1341 struct dirfile **files_end;
1342
1343 files_slot = (struct dirfile **) dir->contents->dirfiles.ht_vec;
1344 files_end = files_slot + dir->contents->dirfiles.ht_size;
1345 for ( ; files_slot < files_end; files_slot++)
1346 {
1347 struct dirfile *df = *files_slot;
1348 if (! HASH_VACANT (df))
1349 {
1350 if (df->impossible)
1351 ++im;
1352 else
1353 ++f;
1354 }
1355 }
1356#ifdef WINDOWS32
1357 printf (_("# %s (key %s, mtime %I64u): "),
1358 dir->name, dir->contents->path_key,
1359 (unsigned long long)dir->contents->mtime);
1360#else /* WINDOWS32 */
1361#ifdef VMS_INO_T
1362 printf (_("# %s (device %d, inode [%d,%d,%d]): "),
1363 dir->name, dir->contents->dev,
1364 dir->contents->ino[0], dir->contents->ino[1],
1365 dir->contents->ino[2]);
1366#else
1367 printf (_("# %s (device %ld, inode %ld): "),
1368 dir->name,
1369 (long)dir->contents->dev, (long)dir->contents->ino);
1370#endif
1371#endif /* WINDOWS32 */
1372 if (f == 0)
1373 fputs (_("No"), stdout);
1374 else
1375 printf ("%u", f);
1376 fputs (_(" files, "), stdout);
1377 if (im == 0)
1378 fputs (_("no"), stdout);
1379 else
1380 printf ("%u", im);
1381 fputs (_(" impossibilities"), stdout);
1382 if (dir->contents->dirstream == 0)
1383 puts (".");
1384 else
1385 puts (_(" so far."));
1386 files += f;
1387 impossible += im;
1388#ifdef KMK
1389 fputs ("# ", stdout);
1390 hash_print_stats (&dir->contents->dirfiles, stdout);
1391 fputs ("\n", stdout);
1392#endif
1393 }
1394 }
1395 }
1396
1397 fputs ("\n# ", stdout);
1398 if (files == 0)
1399 fputs (_("No"), stdout);
1400 else
1401 printf ("%u", files);
1402 fputs (_(" files, "), stdout);
1403 if (impossible == 0)
1404 fputs (_("no"), stdout);
1405 else
1406 printf ("%u", impossible);
1407 printf (_(" impossibilities in %lu directories.\n"), directories.ht_fill);
1408#ifdef KMK
1409 fputs ("# directories: ", stdout);
1410 hash_print_stats (&directories, stdout);
1411 fputs ("\n# directory_contents: ", stdout);
1412 hash_print_stats (&directory_contents, stdout);
1413 fputs ("\n", stdout);
1414#endif
1415}
1416
1417
1418#ifdef CONFIG_WITH_PRINT_STATS_SWITCH
1419/* Print stats */
1420
1421void print_dir_stats (void)
1422{
1423 /** @todo normal dir stats. */
1424}
1425#endif
1426
1427
1428/* Hooks for globbing. */
1429
1430/* Structure describing state of iterating through a directory hash table. */
1431
1432struct dirstream
1433 {
1434 struct directory_contents *contents; /* The directory being read. */
1435 struct dirfile **dirfile_slot; /* Current slot in table. */
1436 };
1437
1438/* Forward declarations. */
1439static __ptr_t open_dirstream (const char *);
1440static struct dirent *read_dirstream (__ptr_t);
1441
1442static __ptr_t
1443open_dirstream (const char *directory)
1444{
1445 struct dirstream *new;
1446 struct directory *dir = find_directory (directory);
1447
1448 if (dir->contents == 0 || dir->contents->dirfiles.ht_vec == 0)
1449 /* DIR->contents is nil if the directory could not be stat'd.
1450 DIR->contents->dirfiles is nil if it could not be opened. */
1451 return 0;
1452
1453 /* Read all the contents of the directory now. There is no benefit
1454 in being lazy, since glob will want to see every file anyway. */
1455
1456 dir_contents_file_exists_p (dir->contents, 0);
1457
1458 new = xmalloc (sizeof (struct dirstream));
1459 new->contents = dir->contents;
1460 new->dirfile_slot = (struct dirfile **) new->contents->dirfiles.ht_vec;
1461
1462 return (__ptr_t) new;
1463}
1464
1465static struct dirent *
1466read_dirstream (__ptr_t stream)
1467{
1468 static char *buf;
1469 static unsigned int bufsz;
1470
1471 struct dirstream *const ds = (struct dirstream *) stream;
1472 struct directory_contents *dc = ds->contents;
1473 struct dirfile **dirfile_end = (struct dirfile **) dc->dirfiles.ht_vec + dc->dirfiles.ht_size;
1474
1475 while (ds->dirfile_slot < dirfile_end)
1476 {
1477 struct dirfile *df = *ds->dirfile_slot++;
1478 if (! HASH_VACANT (df) && !df->impossible)
1479 {
1480 /* The glob interface wants a 'struct dirent', so mock one up. */
1481 struct dirent *d;
1482 unsigned int len = df->length + 1;
1483 unsigned int sz = sizeof (*d) - sizeof (d->d_name) + len;
1484 if (sz > bufsz)
1485 {
1486 bufsz *= 2;
1487 if (sz > bufsz)
1488 bufsz = sz;
1489 buf = xrealloc (buf, bufsz);
1490 }
1491 d = (struct dirent *) buf;
1492#ifdef __MINGW32__
1493# if __MINGW32_MAJOR_VERSION < 3 || (__MINGW32_MAJOR_VERSION == 3 && \
1494 __MINGW32_MINOR_VERSION == 0)
1495 d->d_name = xmalloc (len);
1496# endif
1497#endif
1498 FAKE_DIR_ENTRY (d);
1499#ifdef _DIRENT_HAVE_D_NAMLEN
1500 d->d_namlen = len - 1;
1501#endif
1502#ifdef _DIRENT_HAVE_D_TYPE
1503 d->d_type = DT_UNKNOWN;
1504#endif
1505 memcpy (d->d_name, df->name, len);
1506 return d;
1507 }
1508 }
1509
1510 return 0;
1511}
1512
1513/* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
1514 * macro for stat64(). If stat is a macro, make a local wrapper function to
1515 * invoke it.
1516 *
1517 * On MS-Windows, stat() "succeeds" for foo/bar/. where foo/bar is a
1518 * regular file; fix that here.
1519 */
1520#if !defined(stat) && !defined(WINDOWS32) || defined(VMS)
1521# ifndef VMS
1522# ifndef HAVE_SYS_STAT_H
1523int stat (const char *path, struct stat *sbuf);
1524# endif
1525# else
1526 /* We are done with the fake stat. Go back to the real stat */
1527# ifdef stat
1528# undef stat
1529# endif
1530# endif
1531# define local_stat stat
1532#else
1533static int
1534local_stat (const char *path, struct stat *buf)
1535{
1536 int e;
1537#ifdef WINDOWS32
1538 size_t plen = strlen (path);
1539
1540 /* Make sure the parent of "." exists and is a directory, not a
1541 file. This is because 'stat' on Windows normalizes the argument
1542 foo/. => foo without checking first that foo is a directory. */
1543 if (plen > 1 && path[plen - 1] == '.'
1544 && (path[plen - 2] == '/' || path[plen - 2] == '\\'))
1545 {
1546 char parent[MAXPATHLEN];
1547
1548 strncpy (parent, path, plen - 2);
1549 parent[plen - 2] = '\0';
1550 if (stat (parent, buf) < 0 || !_S_ISDIR (buf->st_mode))
1551 return -1;
1552 }
1553#endif
1554
1555 EINTRLOOP (e, stat (path, buf));
1556 return e;
1557}
1558#endif
1559
1560#ifdef KMK
1561static int dir_exists_p (const char *dirname)
1562{
1563 if (file_exists_p (dirname))
1564 {
1565 struct directory *dir = find_directory (dirname);
1566 if (dir != NULL && dir->contents && dir->contents->dirfiles.ht_vec != NULL)
1567 return 1;
1568 }
1569 return 0;
1570}
1571#endif
1572
1573void
1574dir_setup_glob (glob_t *gl)
1575{
1576 gl->gl_opendir = open_dirstream;
1577 gl->gl_readdir = read_dirstream;
1578 gl->gl_closedir = free;
1579 gl->gl_stat = local_stat;
1580#ifdef __EMX__ /* The FreeBSD implementation actually uses gl_lstat!! */
1581 gl->gl_lstat = local_stat;
1582#endif
1583#ifdef GLOB_WITH_EXTENDED_KMK_MEMBERS
1584 gl->gl_exists = file_exists_p;
1585 gl->gl_isdir = dir_exists_p;
1586#endif
1587 /* We don't bother setting gl_lstat, since glob never calls it.
1588 The slot is only there for compatibility with 4.4 BSD. */
1589}
1590
1591void
1592hash_init_directories (void)
1593{
1594#ifndef CONFIG_WITH_STRCACHE2
1595 hash_init (&directories, DIRECTORY_BUCKETS,
1596 directory_hash_1, directory_hash_2, directory_hash_cmp);
1597#else /* CONFIG_WITH_STRCACHE2 */
1598 hash_init_strcached (&directories, DIRECTORY_BUCKETS, &file_strcache,
1599 offsetof (struct directory, name));
1600#endif /* CONFIG_WITH_STRCACHE2 */
1601 hash_init (&directory_contents, DIRECTORY_BUCKETS,
1602 directory_contents_hash_1, directory_contents_hash_2,
1603 directory_contents_hash_cmp);
1604#ifdef CONFIG_WITH_ALLOC_CACHES
1605 alloccache_init (&directories_cache, sizeof (struct directory),
1606 "directories", NULL, NULL);
1607 alloccache_init (&directory_contents_cache, sizeof (struct directory_contents),
1608 "directory_contents", NULL, NULL);
1609 alloccache_init (&dirfile_cache, sizeof (struct dirfile),
1610 "dirfile", NULL, NULL);
1611#endif /* CONFIG_WITH_ALLOC_CACHES */
1612}
1613
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use