summaryrefslogtreecommitdiff
path: root/src/dired.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dired.c')
-rw-r--r--src/dired.c120
1 files changed, 68 insertions, 52 deletions
diff --git a/src/dired.c b/src/dired.c
index a4c8621e9c0..ed0571fe9fe 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <grp.h>
#include <errno.h>
+#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
@@ -54,6 +55,7 @@ static Lisp_Object Qfile_attributes;
static Lisp_Object Qfile_attributes_lessp;
static ptrdiff_t scmp (const char *, const char *, ptrdiff_t);
+static Lisp_Object file_attributes (int, char const *, Lisp_Object);
/* Return the number of bytes in DP's name. */
static ptrdiff_t
@@ -66,6 +68,44 @@ dirent_namelen (struct dirent *dp)
#endif
}
+static DIR *
+open_directory (char const *name, int *fdp)
+{
+ DIR *d;
+ int fd, opendir_errno;
+
+ block_input ();
+
+#ifdef DOS_NT
+ /* Directories cannot be opened. The emulation assumes that any
+ file descriptor other than AT_FDCWD corresponds to the most
+ recently opened directory. This hack is good enough for Emacs. */
+ fd = 0;
+ d = opendir (name);
+ opendir_errno = errno;
+#else
+ fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
+ if (fd < 0)
+ {
+ opendir_errno = errno;
+ d = 0;
+ }
+ else
+ {
+ d = fdopendir (fd);
+ opendir_errno = errno;
+ if (! d)
+ close (fd);
+ }
+#endif
+
+ unblock_input ();
+
+ *fdp = fd;
+ errno = opendir_errno;
+ return d;
+}
+
#ifdef WINDOWSNT
Lisp_Object
directory_files_internal_w32_unwind (Lisp_Object arg)
@@ -96,6 +136,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
Lisp_Object id_format)
{
DIR *d;
+ int fd;
ptrdiff_t directory_nbytes;
Lisp_Object list, dirfilename, encoded_directory;
struct re_pattern_buffer *bufp = NULL;
@@ -142,9 +183,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
/* Now *bufp is the compiled form of MATCH; don't call anything
which might compile a new regexp until we're done with the loop! */
- block_input ();
- d = opendir (SSDATA (dirfilename));
- unblock_input ();
+ d = open_directory (SSDATA (dirfilename), &fd);
if (d == NULL)
report_file_error ("Opening directory", Fcons (directory, Qnil));
@@ -259,20 +298,9 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
if (attrs)
{
- /* Construct an expanded filename for the directory entry.
- Use the decoded names for input to Ffile_attributes. */
- Lisp_Object decoded_fullname, fileattrs;
- struct gcpro gcpro1, gcpro2;
-
- decoded_fullname = fileattrs = Qnil;
- GCPRO2 (decoded_fullname, fileattrs);
-
- /* Both Fexpand_file_name and Ffile_attributes can GC. */
- decoded_fullname = Fexpand_file_name (name, directory);
- fileattrs = Ffile_attributes (decoded_fullname, id_format);
-
+ Lisp_Object fileattrs
+ = file_attributes (fd, dp->d_name, id_format);
list = Fcons (Fcons (finalname, fileattrs), list);
- UNGCPRO;
}
else
list = Fcons (finalname, list);
@@ -413,8 +441,7 @@ These are all file names in directory DIRECTORY which begin with FILE. */)
return file_name_completion (file, directory, 1, Qnil);
}
-static int file_name_completion_stat (Lisp_Object dirname, struct dirent *dp,
- struct stat *st_addr);
+static int file_name_completion_stat (int, struct dirent *, struct stat *);
static Lisp_Object Qdefault_directory;
static Lisp_Object
@@ -422,6 +449,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
Lisp_Object predicate)
{
DIR *d;
+ int fd;
ptrdiff_t bestmatchsize = 0;
int matchcount = 0;
/* If ALL_FLAG is 1, BESTMATCH is the list of all matches, decoded.
@@ -458,9 +486,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
encoded_dir = ENCODE_FILE (dirname);
- block_input ();
- d = opendir (SSDATA (Fdirectory_file_name (encoded_dir)));
- unblock_input ();
+ d = open_directory (SSDATA (Fdirectory_file_name (encoded_dir)), &fd);
if (!d)
report_file_error ("Opening directory", Fcons (dirname, Qnil));
@@ -495,7 +521,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
SCHARS (encoded_file)))
continue;
- if (file_name_completion_stat (encoded_dir, dp, &st) < 0)
+ if (file_name_completion_stat (fd, dp, &st) < 0)
continue;
directoryp = S_ISDIR (st.st_mode) != 0;
@@ -772,14 +798,9 @@ scmp (const char *s1, const char *s2, ptrdiff_t len)
}
static int
-file_name_completion_stat (Lisp_Object dirname, struct dirent *dp,
- struct stat *st_addr)
+file_name_completion_stat (int fd, struct dirent *dp, struct stat *st_addr)
{
- ptrdiff_t len = dirent_namelen (dp);
- ptrdiff_t pos = SCHARS (dirname);
int value;
- USE_SAFE_ALLOCA;
- char *fullname = SAFE_ALLOCA (len + pos + 2);
#ifdef MSDOS
/* Some fields of struct stat are *very* expensive to compute on MS-DOS,
@@ -792,23 +813,15 @@ file_name_completion_stat (Lisp_Object dirname, struct dirent *dp,
_djstat_flags = _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
#endif /* MSDOS */
- memcpy (fullname, SDATA (dirname), pos);
- if (!IS_DIRECTORY_SEP (fullname[pos - 1]))
- fullname[pos++] = DIRECTORY_SEP;
-
- memcpy (fullname + pos, dp->d_name, len);
- fullname[pos + len] = 0;
-
/* 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 = lstat (fullname, st_addr);
+ value = fstatat (fd, dp->d_name, st_addr, AT_SYMLINK_NOFOLLOW);
if (value == 0 && S_ISLNK (st_addr->st_mode))
- stat (fullname, st_addr);
+ fstatat (fd, dp->d_name, st_addr, 0);
#ifdef MSDOS
_djstat_flags = save_djstat_flags;
#endif /* MSDOS */
- SAFE_FREE ();
return value;
}
@@ -886,18 +899,8 @@ On some FAT-based filesystems, only the date of last access is recorded,
so last access time will always be midnight of that day. */)
(Lisp_Object filename, Lisp_Object id_format)
{
- Lisp_Object values[12];
Lisp_Object encoded;
- struct stat s;
- int lstat_result;
-
- /* An array to hold the mode string generated by filemodestring,
- including its terminating space and null byte. */
- char modes[sizeof "-rwxr-xr-x "];
-
Lisp_Object handler;
- struct gcpro gcpro1;
- char *uname = NULL, *gname = NULL;
filename = Fexpand_file_name (filename, Qnil);
@@ -913,9 +916,22 @@ so last access time will always be midnight of that day. */)
return call3 (handler, Qfile_attributes, filename, id_format);
}
- GCPRO1 (filename);
encoded = ENCODE_FILE (filename);
- UNGCPRO;
+ return file_attributes (AT_FDCWD, SSDATA (encoded), id_format);
+}
+
+static Lisp_Object
+file_attributes (int fd, char const *name, Lisp_Object id_format)
+{
+ Lisp_Object values[12];
+ struct stat s;
+ int lstat_result;
+
+ /* An array to hold the mode string generated by filemodestring,
+ including its terminating space and null byte. */
+ char modes[sizeof "-rwxr-xr-x "];
+
+ char *uname = NULL, *gname = NULL;
#ifdef WINDOWSNT
/* We usually don't request accurate owner and group info, because
@@ -925,7 +941,7 @@ so last access time will always be midnight of that day. */)
w32_stat_get_owner_group = 1;
#endif
- lstat_result = lstat (SSDATA (encoded), &s);
+ lstat_result = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW);
#ifdef WINDOWSNT
w32_stat_get_owner_group = 0;
@@ -934,7 +950,7 @@ so last access time will always be midnight of that day. */)
if (lstat_result < 0)
return Qnil;
- values[0] = (S_ISLNK (s.st_mode) ? Ffile_symlink_p (filename)
+ values[0] = (S_ISLNK (s.st_mode) ? emacs_readlinkat (fd, name)
: S_ISDIR (s.st_mode) ? Qt : Qnil);
values[1] = make_number (s.st_nlink);