diff options
| author | Paul Eggert | 2017-07-31 12:31:02 -0700 |
|---|---|---|
| committer | Paul Eggert | 2017-07-31 12:56:51 -0700 |
| commit | 3a8d0cc825635e07da2a90c4ac987b476fc9b05d (patch) | |
| tree | 7a18ef1a777007f05feff6a35ebadc4338330906 | |
| parent | 192342a3a93a2e467ab589ae2d1ffd5e7acf1398 (diff) | |
| download | emacs-3a8d0cc825635e07da2a90c4ac987b476fc9b05d.tar.gz emacs-3a8d0cc825635e07da2a90c4ac987b476fc9b05d.zip | |
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.
| -rwxr-xr-x | admin/merge-gnulib | 3 | ||||
| -rw-r--r-- | lib/gnulib.mk.in | 2 | ||||
| -rw-r--r-- | m4/d-type.m4 | 32 | ||||
| -rw-r--r-- | m4/gnulib-comp.m4 | 3 | ||||
| -rw-r--r-- | src/dired.c | 71 |
5 files changed, 79 insertions, 32 deletions
diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 18c9ee8def7..c23e8a40ea7 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib | |||
| @@ -30,7 +30,8 @@ GNULIB_MODULES=' | |||
| 30 | careadlinkat close-stream | 30 | careadlinkat close-stream |
| 31 | count-leading-zeros count-one-bits count-trailing-zeros | 31 | count-leading-zeros count-one-bits count-trailing-zeros |
| 32 | crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 | 32 | crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 |
| 33 | diffseq dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat | 33 | d-type diffseq dtoastr dtotimespec dup2 |
| 34 | environ execinfo explicit_bzero faccessat | ||
| 34 | fcntl fcntl-h fdatasync fdopendir | 35 | fcntl fcntl-h fdatasync fdopendir |
| 35 | filemode filevercmp flexmember fstatat fsync | 36 | filemode filevercmp flexmember fstatat fsync |
| 36 | getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog | 37 | getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog |
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 3e57391372a..11c1ecf05ad 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | # the same distribution terms as the rest of that program. | 21 | # the same distribution terms as the rest of that program. |
| 22 | # | 22 | # |
| 23 | # Generated by gnulib-tool. | 23 | # Generated by gnulib-tool. |
| 24 | # 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 | 24 | # 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 |
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | MOSTLYCLEANFILES += core *.stackdump | 27 | MOSTLYCLEANFILES += core *.stackdump |
diff --git a/m4/d-type.m4 b/m4/d-type.m4 new file mode 100644 index 00000000000..c819fc02f84 --- /dev/null +++ b/m4/d-type.m4 | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | # serial 12 | ||
| 2 | |||
| 3 | dnl From Jim Meyering. | ||
| 4 | dnl | ||
| 5 | dnl Check whether struct dirent has a member named d_type. | ||
| 6 | dnl | ||
| 7 | |||
| 8 | # Copyright (C) 1997, 1999-2004, 2006, 2009-2017 Free Software Foundation, Inc. | ||
| 9 | # | ||
| 10 | # This file is free software; the Free Software Foundation | ||
| 11 | # gives unlimited permission to copy and/or distribute it, | ||
| 12 | # with or without modifications, as long as this notice is preserved. | ||
| 13 | |||
| 14 | AC_DEFUN([gl_CHECK_TYPE_STRUCT_DIRENT_D_TYPE], | ||
| 15 | [AC_CACHE_CHECK([for d_type member in directory struct], | ||
| 16 | [gl_cv_struct_dirent_d_type], | ||
| 17 | [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ | ||
| 18 | #include <sys/types.h> | ||
| 19 | #include <dirent.h> | ||
| 20 | ]], | ||
| 21 | [[struct dirent dp; dp.d_type = 0;]])], | ||
| 22 | [gl_cv_struct_dirent_d_type=yes], | ||
| 23 | [gl_cv_struct_dirent_d_type=no]) | ||
| 24 | ] | ||
| 25 | ) | ||
| 26 | if test $gl_cv_struct_dirent_d_type = yes; then | ||
| 27 | AC_DEFINE([HAVE_STRUCT_DIRENT_D_TYPE], [1], | ||
| 28 | [Define if there is a member named d_type in the struct describing | ||
| 29 | directory headers.]) | ||
| 30 | fi | ||
| 31 | ] | ||
| 32 | ) | ||
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 2f135773930..188c116c851 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 | |||
| @@ -61,6 +61,7 @@ AC_DEFUN([gl_EARLY], | |||
| 61 | # Code from module crypto/sha1: | 61 | # Code from module crypto/sha1: |
| 62 | # Code from module crypto/sha256: | 62 | # Code from module crypto/sha256: |
| 63 | # Code from module crypto/sha512: | 63 | # Code from module crypto/sha512: |
| 64 | # Code from module d-type: | ||
| 64 | # Code from module diffseq: | 65 | # Code from module diffseq: |
| 65 | # Code from module dirent: | 66 | # Code from module dirent: |
| 66 | # Code from module dirfd: | 67 | # Code from module dirfd: |
| @@ -199,6 +200,7 @@ AC_DEFUN([gl_INIT], | |||
| 199 | gl_SHA1 | 200 | gl_SHA1 |
| 200 | gl_SHA256 | 201 | gl_SHA256 |
| 201 | gl_SHA512 | 202 | gl_SHA512 |
| 203 | gl_CHECK_TYPE_STRUCT_DIRENT_D_TYPE | ||
| 202 | gl_DIRENT_H | 204 | gl_DIRENT_H |
| 203 | AC_REQUIRE([gl_C99_STRTOLD]) | 205 | AC_REQUIRE([gl_C99_STRTOLD]) |
| 204 | gl_FUNC_DUP2 | 206 | gl_FUNC_DUP2 |
| @@ -968,6 +970,7 @@ AC_DEFUN([gl_FILE_LIST], [ | |||
| 968 | m4/count-leading-zeros.m4 | 970 | m4/count-leading-zeros.m4 |
| 969 | m4/count-one-bits.m4 | 971 | m4/count-one-bits.m4 |
| 970 | m4/count-trailing-zeros.m4 | 972 | m4/count-trailing-zeros.m4 |
| 973 | m4/d-type.m4 | ||
| 971 | m4/dirent_h.m4 | 974 | m4/dirent_h.m4 |
| 972 | m4/dirfd.m4 | 975 | m4/dirfd.m4 |
| 973 | m4/dup2.m4 | 976 | m4/dup2.m4 |
diff --git a/src/dired.c b/src/dired.c index 5ea00fb8db4..288ba6b1038 100644 --- a/src/dired.c +++ b/src/dired.c | |||
| @@ -64,6 +64,21 @@ dirent_namelen (struct dirent *dp) | |||
| 64 | #endif | 64 | #endif |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | #ifndef HAVE_STRUCT_DIRENT_D_TYPE | ||
| 68 | enum { DT_UNKNOWN, DT_DIR, DT_LNK }; | ||
| 69 | #endif | ||
| 70 | |||
| 71 | /* Return the file type of DP. */ | ||
| 72 | static int | ||
| 73 | dirent_type (struct dirent *dp) | ||
| 74 | { | ||
| 75 | #ifdef HAVE_STRUCT_DIRENT_D_TYPE | ||
| 76 | return dp->d_type; | ||
| 77 | #else | ||
| 78 | return DT_UNKNOWN; | ||
| 79 | #endif | ||
| 80 | } | ||
| 81 | |||
| 67 | static DIR * | 82 | static DIR * |
| 68 | open_directory (Lisp_Object dirname, int *fdp) | 83 | open_directory (Lisp_Object dirname, int *fdp) |
| 69 | { | 84 | { |
| @@ -434,7 +449,7 @@ is matched against file and directory names relative to DIRECTORY. */) | |||
| 434 | return file_name_completion (file, directory, 1, Qnil); | 449 | return file_name_completion (file, directory, 1, Qnil); |
| 435 | } | 450 | } |
| 436 | 451 | ||
| 437 | static int file_name_completion_stat (int, struct dirent *, struct stat *); | 452 | static bool file_name_completion_dirp (int, struct dirent *, ptrdiff_t); |
| 438 | 453 | ||
| 439 | static Lisp_Object | 454 | static Lisp_Object |
| 440 | file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag, | 455 | 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, | |||
| 448 | Lisp_Object bestmatch, tem, elt, name; | 463 | Lisp_Object bestmatch, tem, elt, name; |
| 449 | Lisp_Object encoded_file; | 464 | Lisp_Object encoded_file; |
| 450 | Lisp_Object encoded_dir; | 465 | Lisp_Object encoded_dir; |
| 451 | struct stat st; | ||
| 452 | bool directoryp; | 466 | bool directoryp; |
| 453 | /* If not INCLUDEALL, exclude files in completion-ignored-extensions as | 467 | /* If not INCLUDEALL, exclude files in completion-ignored-extensions as |
| 454 | well as "." and "..". Until shown otherwise, assume we can't exclude | 468 | 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, | |||
| 512 | >= 0)) | 526 | >= 0)) |
| 513 | continue; | 527 | continue; |
| 514 | 528 | ||
| 515 | if (file_name_completion_stat (fd, dp, &st) < 0) | 529 | switch (dirent_type (dp)) |
| 516 | continue; | 530 | { |
| 531 | case DT_DIR: | ||
| 532 | directoryp = true; | ||
| 533 | break; | ||
| 534 | |||
| 535 | case DT_LNK: case DT_UNKNOWN: | ||
| 536 | directoryp = file_name_completion_dirp (fd, dp, len); | ||
| 537 | break; | ||
| 538 | |||
| 539 | default: | ||
| 540 | directoryp = false; | ||
| 541 | break; | ||
| 542 | } | ||
| 517 | 543 | ||
| 518 | directoryp = S_ISDIR (st.st_mode) != 0; | ||
| 519 | tem = Qnil; | 544 | tem = Qnil; |
| 520 | /* If all_flag is set, always include all. | 545 | /* If all_flag is set, always include all. |
| 521 | It would not actually be helpful to the user to ignore any possible | 546 | 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) | |||
| 781 | return len - l; | 806 | return len - l; |
| 782 | } | 807 | } |
| 783 | 808 | ||
| 784 | static int | 809 | /* Return true if in the directory FD the directory entry DP, whose |
| 785 | file_name_completion_stat (int fd, struct dirent *dp, struct stat *st_addr) | 810 | string length is LEN, is that of a subdirectory that can be searched. */ |
| 811 | static bool | ||
| 812 | file_name_completion_dirp (int fd, struct dirent *dp, ptrdiff_t len) | ||
| 786 | { | 813 | { |
| 787 | int value; | 814 | USE_SAFE_ALLOCA; |
| 788 | 815 | char *subdir_name = SAFE_ALLOCA (len + 2); | |
| 789 | #ifdef MSDOS | 816 | memcpy (subdir_name, dp->d_name, len); |
| 790 | /* Some fields of struct stat are *very* expensive to compute on MS-DOS, | 817 | strcpy (subdir_name + len, "/"); |
| 791 | but aren't required here. Avoid computing the following fields: | 818 | bool dirp = faccessat (fd, subdir_name, F_OK, AT_EACCESS) == 0; |
| 792 | st_inode, st_size and st_nlink for directories, and the execute bits | 819 | SAFE_FREE (); |
| 793 | in st_mode for non-directory files with non-standard extensions. */ | 820 | return dirp; |
| 794 | |||
| 795 | unsigned short save_djstat_flags = _djstat_flags; | ||
| 796 | |||
| 797 | _djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; | ||
| 798 | #endif /* MSDOS */ | ||
| 799 | |||
| 800 | /* We want to return success if a link points to a nonexistent file, | ||
| 801 | but we want to return the status for what the link points to, | ||
| 802 | in case it is a directory. */ | ||
| 803 | value = fstatat (fd, dp->d_name, st_addr, AT_SYMLINK_NOFOLLOW); | ||
| 804 | if (value == 0 && S_ISLNK (st_addr->st_mode)) | ||
| 805 | fstatat (fd, dp->d_name, st_addr, 0); | ||
| 806 | #ifdef MSDOS | ||
| 807 | _djstat_flags = save_djstat_flags; | ||
| 808 | #endif /* MSDOS */ | ||
| 809 | return value; | ||
| 810 | } | 821 | } |
| 811 | 822 | ||
| 812 | static char * | 823 | static char * |