* progmodes/compile.el (compilation-mode-hook)
[bpt/emacs.git] / src / dired.c
index d9868d7..ed0571f 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.  */
 #include <grp.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 #include <dirent.h>
@@ -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);
 \f
 /* 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)
@@ -258,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);
@@ -412,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
@@ -421,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.
@@ -457,14 +486,12 @@ 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));
 
   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) */
@@ -494,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;
@@ -771,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,
@@ -791,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;
 }
 \f
@@ -885,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);
 
@@ -912,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
@@ -924,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;
@@ -933,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);