Avoid most stat calls when completing file names
* admin/merge-gnulib (GNULIB_MODULES): Add d-type. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * m4/d-type.m4: New file, copied from gnulib. * src/dired.c (DT_UNKNOWN, DT_DIR, DT_LINK) [!HAVE_STRUCT_DIRENT_D_TYPE]: New constants. (dirent_type): New function. (file_name_completion): Use it, to avoid unnecessary calls to stat-like functions on GNU/Linux and other platforms with d_type. (file_name_completion_stat): Just follow the link; there is no need to try first with AT_SYMLINK_NOFOLLOW since the directory entry was already checked to exist.
This commit is contained in:
parent
192342a3a9
commit
3a8d0cc825
5 changed files with 79 additions and 32 deletions
|
@ -30,7 +30,8 @@ GNULIB_MODULES='
|
|||
careadlinkat close-stream
|
||||
count-leading-zeros count-one-bits count-trailing-zeros
|
||||
crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
|
||||
diffseq dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat
|
||||
d-type diffseq dtoastr dtotimespec dup2
|
||||
environ execinfo explicit_bzero faccessat
|
||||
fcntl fcntl-h fdatasync fdopendir
|
||||
filemode filevercmp flexmember fstatat fsync
|
||||
getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
# the same distribution terms as the rest of that program.
|
||||
#
|
||||
# Generated by gnulib-tool.
|
||||
# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask --avoid=stat --avoid=stdarg --avoid=stdbool --avoid=threadlib --avoid=tzset --avoid=unsetenv --avoid=utime --avoid=utime-h --gnu-make --makefile-name=gnulib.mk.in --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-leading-zeros count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 diffseq dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat fcntl fcntl-h fdatasync fdopendir filemode filevercmp flexmember fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ignore-value intprops largefile lstat manywarnings memrchr minmax mkostemp mktime nstrftime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stddef stdio stpcpy strtoimax symlink sys_stat sys_time time time_r time_rz timegm timer-time timespec-add timespec-sub unlocked-io update-copyright utimens vla warnings
|
||||
# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask --avoid=stat --avoid=stdarg --avoid=stdbool --avoid=threadlib --avoid=tzset --avoid=unsetenv --avoid=utime --avoid=utime-h --gnu-make --makefile-name=gnulib.mk.in --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-leading-zeros count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 d-type diffseq dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat fcntl fcntl-h fdatasync fdopendir filemode filevercmp flexmember fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ignore-value intprops largefile lstat manywarnings memrchr minmax mkostemp mktime nstrftime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stddef stdio stpcpy strtoimax symlink sys_stat sys_time time time_r time_rz timegm timer-time timespec-add timespec-sub unlocked-io update-copyright utimens vla warnings
|
||||
|
||||
|
||||
MOSTLYCLEANFILES += core *.stackdump
|
||||
|
|
32
m4/d-type.m4
Normal file
32
m4/d-type.m4
Normal file
|
@ -0,0 +1,32 @@
|
|||
# serial 12
|
||||
|
||||
dnl From Jim Meyering.
|
||||
dnl
|
||||
dnl Check whether struct dirent has a member named d_type.
|
||||
dnl
|
||||
|
||||
# Copyright (C) 1997, 1999-2004, 2006, 2009-2017 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
AC_DEFUN([gl_CHECK_TYPE_STRUCT_DIRENT_D_TYPE],
|
||||
[AC_CACHE_CHECK([for d_type member in directory struct],
|
||||
[gl_cv_struct_dirent_d_type],
|
||||
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
]],
|
||||
[[struct dirent dp; dp.d_type = 0;]])],
|
||||
[gl_cv_struct_dirent_d_type=yes],
|
||||
[gl_cv_struct_dirent_d_type=no])
|
||||
]
|
||||
)
|
||||
if test $gl_cv_struct_dirent_d_type = yes; then
|
||||
AC_DEFINE([HAVE_STRUCT_DIRENT_D_TYPE], [1],
|
||||
[Define if there is a member named d_type in the struct describing
|
||||
directory headers.])
|
||||
fi
|
||||
]
|
||||
)
|
|
@ -61,6 +61,7 @@ AC_DEFUN([gl_EARLY],
|
|||
# Code from module crypto/sha1:
|
||||
# Code from module crypto/sha256:
|
||||
# Code from module crypto/sha512:
|
||||
# Code from module d-type:
|
||||
# Code from module diffseq:
|
||||
# Code from module dirent:
|
||||
# Code from module dirfd:
|
||||
|
@ -199,6 +200,7 @@ AC_DEFUN([gl_INIT],
|
|||
gl_SHA1
|
||||
gl_SHA256
|
||||
gl_SHA512
|
||||
gl_CHECK_TYPE_STRUCT_DIRENT_D_TYPE
|
||||
gl_DIRENT_H
|
||||
AC_REQUIRE([gl_C99_STRTOLD])
|
||||
gl_FUNC_DUP2
|
||||
|
@ -968,6 +970,7 @@ AC_DEFUN([gl_FILE_LIST], [
|
|||
m4/count-leading-zeros.m4
|
||||
m4/count-one-bits.m4
|
||||
m4/count-trailing-zeros.m4
|
||||
m4/d-type.m4
|
||||
m4/dirent_h.m4
|
||||
m4/dirfd.m4
|
||||
m4/dup2.m4
|
||||
|
|
71
src/dired.c
71
src/dired.c
|
@ -64,6 +64,21 @@ dirent_namelen (struct dirent *dp)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifndef HAVE_STRUCT_DIRENT_D_TYPE
|
||||
enum { DT_UNKNOWN, DT_DIR, DT_LNK };
|
||||
#endif
|
||||
|
||||
/* Return the file type of DP. */
|
||||
static int
|
||||
dirent_type (struct dirent *dp)
|
||||
{
|
||||
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
|
||||
return dp->d_type;
|
||||
#else
|
||||
return DT_UNKNOWN;
|
||||
#endif
|
||||
}
|
||||
|
||||
static DIR *
|
||||
open_directory (Lisp_Object dirname, int *fdp)
|
||||
{
|
||||
|
@ -434,7 +449,7 @@ is matched against file and directory names relative to DIRECTORY. */)
|
|||
return file_name_completion (file, directory, 1, Qnil);
|
||||
}
|
||||
|
||||
static int file_name_completion_stat (int, struct dirent *, struct stat *);
|
||||
static bool file_name_completion_dirp (int, struct dirent *, ptrdiff_t);
|
||||
|
||||
static Lisp_Object
|
||||
file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
|
||||
|
@ -448,7 +463,6 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
|
|||
Lisp_Object bestmatch, tem, elt, name;
|
||||
Lisp_Object encoded_file;
|
||||
Lisp_Object encoded_dir;
|
||||
struct stat st;
|
||||
bool directoryp;
|
||||
/* If not INCLUDEALL, exclude files in completion-ignored-extensions as
|
||||
well as "." and "..". Until shown otherwise, assume we can't exclude
|
||||
|
@ -512,10 +526,21 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
|
|||
>= 0))
|
||||
continue;
|
||||
|
||||
if (file_name_completion_stat (fd, dp, &st) < 0)
|
||||
continue;
|
||||
switch (dirent_type (dp))
|
||||
{
|
||||
case DT_DIR:
|
||||
directoryp = true;
|
||||
break;
|
||||
|
||||
case DT_LNK: case DT_UNKNOWN:
|
||||
directoryp = file_name_completion_dirp (fd, dp, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
directoryp = false;
|
||||
break;
|
||||
}
|
||||
|
||||
directoryp = S_ISDIR (st.st_mode) != 0;
|
||||
tem = Qnil;
|
||||
/* If all_flag is set, always include all.
|
||||
It would not actually be helpful to the user to ignore any possible
|
||||
|
@ -781,32 +806,18 @@ scmp (const char *s1, const char *s2, ptrdiff_t len)
|
|||
return len - l;
|
||||
}
|
||||
|
||||
static int
|
||||
file_name_completion_stat (int fd, struct dirent *dp, struct stat *st_addr)
|
||||
/* Return true if in the directory FD the directory entry DP, whose
|
||||
string length is LEN, is that of a subdirectory that can be searched. */
|
||||
static bool
|
||||
file_name_completion_dirp (int fd, struct dirent *dp, ptrdiff_t len)
|
||||
{
|
||||
int value;
|
||||
|
||||
#ifdef MSDOS
|
||||
/* Some fields of struct stat are *very* expensive to compute on MS-DOS,
|
||||
but aren't required here. Avoid computing the following fields:
|
||||
st_inode, st_size and st_nlink for directories, and the execute bits
|
||||
in st_mode for non-directory files with non-standard extensions. */
|
||||
|
||||
unsigned short save_djstat_flags = _djstat_flags;
|
||||
|
||||
_djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
|
||||
#endif /* MSDOS */
|
||||
|
||||
/* We want to return success if a link points to a nonexistent file,
|
||||
but we want to return the status for what the link points to,
|
||||
in case it is a directory. */
|
||||
value = fstatat (fd, dp->d_name, st_addr, AT_SYMLINK_NOFOLLOW);
|
||||
if (value == 0 && S_ISLNK (st_addr->st_mode))
|
||||
fstatat (fd, dp->d_name, st_addr, 0);
|
||||
#ifdef MSDOS
|
||||
_djstat_flags = save_djstat_flags;
|
||||
#endif /* MSDOS */
|
||||
return value;
|
||||
USE_SAFE_ALLOCA;
|
||||
char *subdir_name = SAFE_ALLOCA (len + 2);
|
||||
memcpy (subdir_name, dp->d_name, len);
|
||||
strcpy (subdir_name + len, "/");
|
||||
bool dirp = faccessat (fd, subdir_name, F_OK, AT_EACCESS) == 0;
|
||||
SAFE_FREE ();
|
||||
return dirp;
|
||||
}
|
||||
|
||||
static char *
|
||||
|
|
Loading…
Add table
Reference in a new issue