X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/d125ca15f3751f36a1ec49e3d9f9de1735b43698..e7ac588e198385a9bc5a2338000ab6db69c2353c:/src/dired.c
diff --git a/src/dired.c b/src/dired.c
index 3530b74ecb..0e37568f21 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -1,5 +1,6 @@
/* Lisp functions for making directory listings.
- Copyright (C) 1985-1986, 1993-1994, 1999-2012 Free Software Foundation, Inc.
+ Copyright (C) 1985-1986, 1993-1994, 1999-2013 Free Software
+ Foundation, Inc.
This file is part of GNU Emacs.
@@ -29,6 +30,7 @@ along with GNU Emacs. If not, see . */
#include
#include
+#include
#include
#include
@@ -53,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
@@ -65,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)
@@ -77,7 +118,7 @@ directory_files_internal_w32_unwind (Lisp_Object arg)
static Lisp_Object
directory_files_internal_unwind (Lisp_Object dh)
{
- DIR *d = (DIR *) XSAVE_VALUE (dh)->pointer;
+ DIR *d = XSAVE_POINTER (dh, 0);
block_input ();
closedir (d);
unblock_input ();
@@ -95,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;
@@ -141,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));
@@ -151,7 +191,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
file-attributes on filenames, both of which can throw, so we must
do a proper unwind-protect. */
record_unwind_protect (directory_files_internal_unwind,
- make_save_value (d, 0));
+ make_save_pointer (d));
#ifdef WINDOWSNT
if (attrs)
@@ -193,19 +233,15 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
errno = 0;
dp = readdir (d);
-
- if (dp == NULL && (0
-#ifdef EAGAIN
- || errno == EAGAIN
-#endif
-#ifdef EINTR
- || errno == EINTR
-#endif
- ))
- { QUIT; continue; }
-
- if (dp == NULL)
- break;
+ if (!dp)
+ {
+ if (errno == EAGAIN || errno == EINTR)
+ {
+ QUIT;
+ continue;
+ }
+ break;
+ }
len = dirent_namelen (dp);
name = finalname = make_unibyte_string (dp->d_name, len);
@@ -262,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);
@@ -416,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
@@ -425,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.
@@ -459,16 +484,14 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
on the encoded file name. */
encoded_file = STRING_MULTIBYTE (file) ? ENCODE_FILE (file) : file;
- encoded_dir = ENCODE_FILE (dirname);
+ encoded_dir = ENCODE_FILE (Fdirectory_file_name (dirname));
- block_input ();
- d = opendir (SSDATA (Fdirectory_file_name (encoded_dir)));
- unblock_input ();
+ d = open_directory (SSDATA (encoded_dir), &fd);
if (!d)
report_file_error ("Opening directory", Fcons (dirname, Qnil));
record_unwind_protect (directory_files_internal_unwind,
- make_save_value (d, 0));
+ make_save_pointer (d));
/* Loop reading blocks */
/* (att3b compiler bug requires do a null comparison this way) */
@@ -480,17 +503,15 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
errno = 0;
dp = readdir (d);
- if (dp == NULL && (0
-# ifdef EAGAIN
- || errno == EAGAIN
-# endif
-# ifdef EINTR
- || errno == EINTR
-# endif
- ))
- { QUIT; continue; }
-
- if (!dp) break;
+ if (!dp)
+ {
+ if (errno == EAGAIN || errno == EINTR)
+ {
+ QUIT;
+ continue;
+ }
+ break;
+ }
len = dirent_namelen (dp);
@@ -500,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;
@@ -677,10 +698,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
name, zero,
make_number (compare),
completion_ignore_case ? Qt : Qnil);
- ptrdiff_t matchsize
- = (EQ (cmp, Qt) ? compare
- : XINT (cmp) < 0 ? - XINT (cmp) - 1
- : XINT (cmp) - 1);
+ ptrdiff_t matchsize = EQ (cmp, Qt) ? compare : eabs (XINT (cmp)) - 1;
if (completion_ignore_case)
{
@@ -780,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,
@@ -800,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;
}
@@ -826,7 +831,7 @@ stat_uname (struct stat *st)
#ifdef WINDOWSNT
return st->st_uname;
#else
- struct passwd *pw = (struct passwd *) getpwuid (st->st_uid);
+ struct passwd *pw = getpwuid (st->st_uid);
if (pw)
return pw->pw_name;
@@ -841,7 +846,7 @@ stat_gname (struct stat *st)
#ifdef WINDOWSNT
return st->st_gname;
#else
- struct group *gr = (struct group *) getgrgid (st->st_gid);
+ struct group *gr = getgrgid (st->st_gid);
if (gr)
return gr->gr_name;
@@ -875,7 +880,7 @@ Elements of the attribute list are:
7. Size in bytes.
This is a floating point number if the size is too large for an integer.
8. File modes, as a string of ten letters or dashes as in ls -l.
- 9. t if file's gid would change if file were deleted and recreated.
+ 9. An unspecified value, present only for backward compatibility.
10. inode number. If it is larger than what an Emacs integer can hold,
this is of the form (HIGH . LOW): first the high bits, then the low 16 bits.
If even HIGH is too large for an Emacs integer, this is instead of the form
@@ -894,21 +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;
-#ifdef BSD4_2
- Lisp_Object dirname;
- struct stat sdir;
-#endif /* BSD4_2 */
-
- /* 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);
@@ -924,14 +916,41 @@ 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
+ it can be very expensive on Windows to get that, and most callers
+ of 'lstat' don't need that. But here we do want that information
+ to be accurate. */
+ w32_stat_get_owner_group = 1;
+#endif
+
+ lstat_result = fstatat (fd, name, &s, AT_SYMLINK_NOFOLLOW);
+
+#ifdef WINDOWSNT
+ w32_stat_get_owner_group = 0;
+#endif
- if (lstat (SSDATA (encoded), &s) < 0)
+ 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);
@@ -965,17 +984,7 @@ so last access time will always be midnight of that day. */)
filemodestring (&s, modes);
values[8] = make_string (modes, 10);
-#ifdef BSD4_2 /* file gid will be dir gid */
- dirname = Ffile_name_directory (filename);
- if (! NILP (dirname))
- encoded = ENCODE_FILE (dirname);
- if (! NILP (dirname) && stat (SDATA (encoded), &sdir) == 0)
- values[9] = (sdir.st_gid != s.st_gid) ? Qt : Qnil;
- else /* if we can't tell, assume worst */
- values[9] = Qt;
-#else /* file gid will be egid */
- values[9] = (s.st_gid != getegid ()) ? Qt : Qnil;
-#endif /* not BSD4_2 */
+ values[9] = Qt;
values[10] = INTEGER_TO_CONS (s.st_ino);
values[11] = INTEGER_TO_CONS (s.st_dev);