📄 dir.c
字号:
/* Directory hashing for GNU Make.Copyright (C) 1988,89,91,92,93,94,95,96,97 Free Software Foundation, Inc.This file is part of GNU Make.GNU Make is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU Make is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU Make; see the file COPYING. If not, write tothe Free Software Foundation, Inc., 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include "make.h"#ifdef HAVE_DIRENT_H# include <dirent.h># define NAMLEN(dirent) strlen((dirent)->d_name)#else# define dirent direct# define NAMLEN(dirent) (dirent)->d_namlen# ifdef HAVE_SYS_NDIR_H# include <sys/ndir.h># endif# ifdef HAVE_SYS_DIR_H# include <sys/dir.h># endif# ifdef HAVE_NDIR_H# include <ndir.h># endif# ifdef HAVE_VMSDIR_H# include "vmsdir.h"# endif /* HAVE_VMSDIR_H */#endif/* In GNU systems, <dirent.h> defines this macro for us. */#ifdef _D_NAMLEN#undef NAMLEN#define NAMLEN(d) _D_NAMLEN(d)#endif#if (defined (POSIX) || defined (WINDOWS32)) && !defined (__GNU_LIBRARY__)/* Posix does not require that the d_ino field be present, and some systems do not provide it. */#define REAL_DIR_ENTRY(dp) 1#define FAKE_DIR_ENTRY(dp)#else#define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)#define FAKE_DIR_ENTRY(dp) (dp->d_ino = 1)#endif /* POSIX */#ifdef __MSDOS__#include <ctype.h>#include <fcntl.h>/* If it's MSDOS that doesn't have _USE_LFN, disable LFN support. */#ifndef _USE_LFN#define _USE_LFN 0#endifstatic char *dosify (filename) char *filename;{ static char dos_filename[14]; char *df; int i; if (filename == 0 || _USE_LFN) return filename; /* FIXME: what about filenames which violate 8+3 constraints, like "config.h.in", or ".emacs"? */ if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0) return filename; df = dos_filename; /* First, transform the name part. */ for (i = 0; *filename != '\0' && i < 8 && *filename != '.'; ++i) *df++ = tolower (*filename++); /* Now skip to the next dot. */ while (*filename != '\0' && *filename != '.') ++filename; if (*filename != '\0') { *df++ = *filename++; for (i = 0; *filename != '\0' && i < 3 && *filename != '.'; ++i) *df++ = tolower (*filename++); } /* Look for more dots. */ while (*filename != '\0' && *filename != '.') ++filename; if (*filename == '.') return filename; *df = 0; return dos_filename;}#endif /* __MSDOS__ */#ifdef WINDOWS32#include "pathstuff.h"#endif#ifdef _AMIGA#include <ctype.h>#endif#ifdef HAVE_CASE_INSENSITIVE_FSstatic char *downcase (filename) char *filename;{#ifdef _AMIGA static char new_filename[136];#else static char new_filename[PATH_MAX];#endif char *df; int i; if (filename == 0) return 0; df = new_filename; /* First, transform the name part. */ for (i = 0; *filename != '\0'; ++i) { *df++ = tolower (*filename); ++filename; } *df = 0; return new_filename;}#endif /* HAVE_CASE_INSENSITIVE_FS */#ifdef VMSstatic intvms_hash (name) char *name;{ int h = 0; int g; while (*name) { h = (h << 4) + *name++; g = h & 0xf0000000; if (g) { h = h ^ (g >> 24); h = h ^ g; } } return h;}/* fake stat entry for a directory */static intvmsstat_dir (name, st) char *name; struct stat *st;{ char *s; int h; DIR *dir; dir = opendir (name); if (dir == 0) return -1; closedir (dir); s = strchr (name, ':'); /* find device */ if (s) { *s++ = 0; st->st_dev = (char *)vms_hash (name); h = vms_hash (s); *(s-1) = ':'; } else { st->st_dev = 0; s = name; h = vms_hash (s); } st->st_ino[0] = h & 0xff; st->st_ino[1] = h & 0xff00; st->st_ino[2] = h >> 16; return 0;}#endif /* VMS *//* Hash table of directories. */#ifndef DIRECTORY_BUCKETS#define DIRECTORY_BUCKETS 199#endifstruct directory_contents { struct directory_contents *next; dev_t dev; /* Device and inode numbers of this dir. */#ifdef WINDOWS32 /* * Inode means nothing on WINDOWS32. Even file key information is * unreliable because it is random per file open and undefined * for remote filesystems. The most unique attribute I can * come up with is the fully qualified name of the directory. Beware * though, this is also unreliable. I'm open to suggestion on a better * way to emulate inode. */ char *path_key; int mtime; /* controls check for stale directory cache */ int fs_flags; /* FS_FAT, FS_NTFS, ... */#define FS_FAT 0x1#define FS_NTFS 0x2#define FS_UNKNOWN 0x4#else#ifdef VMS ino_t ino[3];#else ino_t ino;#endif#endif /* WINDOWS32 */ struct dirfile **files; /* Files in this directory. */ DIR *dirstream; /* Stream reading this directory. */ };/* Table of directory contents hashed by device and inode number. */static struct directory_contents *directories_contents[DIRECTORY_BUCKETS];struct directory { struct directory *next; char *name; /* Name of the directory. */ /* The directory's contents. This data may be shared by several entries in the hash table, which refer to the same directory (identified uniquely by `dev' and `ino') under different names. */ struct directory_contents *contents; };/* Table of directories hashed by name. */static struct directory *directories[DIRECTORY_BUCKETS];/* Never have more than this many directories open at once. */#define MAX_OPEN_DIRECTORIES 10static unsigned int open_directories = 0;/* Hash table of files in each directory. */struct dirfile { struct dirfile *next; char *name; /* Name of the file. */ char impossible; /* This file is impossible. */ };#ifndef DIRFILE_BUCKETS#define DIRFILE_BUCKETS 107#endifstatic int dir_contents_file_exists_p PARAMS ((struct directory_contents *dir, char *filename));static struct directory *find_directory PARAMS ((char *name));/* Find the directory named NAME and return its `struct directory'. */static struct directory *find_directory (name) register char *name;{ register unsigned int hash = 0; register char *p; register struct directory *dir;#ifdef WINDOWS32 char* w32_path; char fs_label[BUFSIZ]; char fs_type[BUFSIZ]; long fs_serno; long fs_flags; long fs_len;#endif#ifdef VMS if ((*name == '.') && (*(name+1) == 0)) name = "[]"; else name = vmsify (name,1);#endif for (p = name; *p != '\0'; ++p) HASHI (hash, *p); hash %= DIRECTORY_BUCKETS; for (dir = directories[hash]; dir != 0; dir = dir->next) if (strieq (dir->name, name)) break; if (dir == 0) { struct stat st; /* The directory was not found. Create a new entry for it. */ dir = (struct directory *) xmalloc (sizeof (struct directory)); dir->next = directories[hash]; directories[hash] = dir; dir->name = savestring (name, p - name); /* The directory is not in the name hash table. Find its device and inode numbers, and look it up by them. */#ifdef VMS if (vmsstat_dir (name, &st) < 0)#else#ifdef WINDOWS32 /* Remove any trailing '\'. Windows32 stat fails even on valid directories if they end in '\'. */ if (p[-1] == '\\') p[-1] = '\0';#endif if (stat (name, &st) < 0)#endif { /* Couldn't stat the directory. Mark this by setting the `contents' member to a nil pointer. */ dir->contents = 0; } else { /* Search the contents hash table; device and inode are the key. */ struct directory_contents *dc;#ifdef WINDOWS32 w32_path = w32ify(name, 1); hash = ((unsigned int) st.st_dev << 16) | (unsigned int) st.st_ctime;#else#ifdef VMS hash = ((unsigned int) st.st_dev << 16) | ((unsigned int) st.st_ino[0] + (unsigned int) st.st_ino[1] + (unsigned int) st.st_ino[2]);#else hash = ((unsigned int) st.st_dev << 16) | (unsigned int) st.st_ino;#endif#endif hash %= DIRECTORY_BUCKETS; for (dc = directories_contents[hash]; dc != 0; dc = dc->next)#ifdef WINDOWS32 if (strieq(dc->path_key, w32_path))#else if (dc->dev == st.st_dev#ifdef VMS && dc->ino[0] == st.st_ino[0] && dc->ino[1] == st.st_ino[1] && dc->ino[2] == st.st_ino[2])#else && dc->ino == st.st_ino)#endif#endif /* WINDOWS32 */ break; if (dc == 0) { /* Nope; this really is a directory we haven't seen before. */ dc = (struct directory_contents *) xmalloc (sizeof (struct directory_contents)); /* Enter it in the contents hash table. */ dc->dev = st.st_dev;#ifdef WINDOWS32 dc->path_key = xstrdup(w32_path); dc->mtime = st.st_mtime; /* * NTFS is the only WINDOWS32 filesystem that bumps mtime * on a directory when files are added/deleted from * a directory. */ w32_path[3] = '\0'; if (GetVolumeInformation(w32_path, fs_label, sizeof (fs_label), &fs_serno, &fs_len, &fs_flags, fs_type, sizeof (fs_type)) == FALSE) dc->fs_flags = FS_UNKNOWN; else if (!strcmp(fs_type, "FAT")) dc->fs_flags = FS_FAT; else if (!strcmp(fs_type, "NTFS")) dc->fs_flags = FS_NTFS; else dc->fs_flags = FS_UNKNOWN;#else#ifdef VMS dc->ino[0] = st.st_ino[0]; dc->ino[1] = st.st_ino[1]; dc->ino[2] = st.st_ino[2];#else dc->ino = st.st_ino;#endif#endif /* WINDOWS32 */ dc->next = directories_contents[hash]; directories_contents[hash] = dc; dc->dirstream = opendir (name); if (dc->dirstream == 0) { /* Couldn't open the directory. Mark this by setting the `files' member to a nil pointer. */ dc->files = 0; } else { /* Allocate an array of buckets for files and zero it. */ dc->files = (struct dirfile **) xmalloc (sizeof (struct dirfile *) * DIRFILE_BUCKETS); bzero ((char *) dc->files, sizeof (struct dirfile *) * DIRFILE_BUCKETS); /* Keep track of how many directories are open. */ ++open_directories; if (open_directories == MAX_OPEN_DIRECTORIES) /* We have too many directories open already. Read the entire directory and then close it. */ (void) dir_contents_file_exists_p (dc, (char *) 0); } } /* Point the name-hashed entry for DIR at its contents data. */ dir->contents = dc; } } return dir;}/* Return 1 if the name FILENAME is entered in DIR's hash table. FILENAME must contain no slashes. */static intdir_contents_file_exists_p (dir, filename) register struct directory_contents *dir; register char *filename;{ register unsigned int hash; register char *p; register struct dirfile *df; register struct dirent *d;#ifdef WINDOWS32 struct stat st; int rehash = 0;#endif if (dir == 0 || dir->files == 0) { /* The directory could not be stat'd or opened. */ return 0; }#ifdef __MSDOS__ filename = dosify (filename);#endif#ifdef HAVE_CASE_INSENSITIVE_FS filename = downcase (filename);#endif#ifdef VMS filename = vmsify (filename,0);#endif hash = 0; if (filename != 0) { if (*filename == '\0') { /* Checking if the directory exists. */ return 1; } for (p = filename; *p != '\0'; ++p) HASH (hash, *p); hash %= DIRFILE_BUCKETS; /* Search the list of hashed files. */ for (df = dir->files[hash]; df != 0; df = df->next) { if (strieq (df->name, filename)) { return !df->impossible; } } } /* The file was not found in the hashed list. Try to read the directory further. */ if (dir->dirstream == 0) {#ifdef WINDOWS32 /* * Check to see if directory has changed since last read. FAT * filesystems force a rehash always as mtime does not change * on directories (ugh!). */ if (dir->path_key && (dir->fs_flags & FS_FAT || (stat(dir->path_key, &st) == 0 && st.st_mtime > dir->mtime))) { /* reset date stamp to show most recent re-process */ dir->mtime = st.st_mtime; /* make sure directory can still be opened */ dir->dirstream = opendir(dir->path_key); if (dir->dirstream) rehash = 1; else return 0; /* couldn't re-read - fail */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -