VirtualBox

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

Last change on this file since 2702 was 2702, checked in by bird, 12 years ago

kmk/WindowsNT: Avoiding unnecessary stat() calls. Reimplemented stat(), lstat(), fstat(), opendir(), readdir(), and closedir() using native NT APIs.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette