unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
@ 2013-01-24  8:26 Paul Eggert
  2013-01-24 17:28 ` Eli Zaretskii
  0 siblings, 1 reply; 9+ messages in thread
From: Paul Eggert @ 2013-01-24  8:26 UTC (permalink / raw)
  To: 13539

[-- Attachment #1: Type: text/plain, Size: 665 bytes --]

Attached is a patch that can greatly improve the performance of
directory-files-and-attributes, which I'd like to install
into the trunk.  The key idea is to use fstatat
instead of lstat; this reduces directory-reading
from an O(ND) to an O(N) operation, where N is the number
of directory entries and D is the length of
the directory's name.

This patch works on POSIXish hosts, but it'll no doubt
need a few changes to the MS-Windows makefiles to compile
fdopendir.c, fstatat.c, openat-die.c, save-cwd.c, openat-proc.c,
or whatever subset of these is needed on MS-Windows.
I'll CC: this to Eli to give him a heads-up.

This patch is relative to trunk bzr 111595.

[-- Attachment #2: fdopendir.txt --]
[-- Type: text/plain, Size: 73372 bytes --]

=== modified file '.bzrignore'
--- .bzrignore	2013-01-23 23:49:46 +0000
+++ .bzrignore	2013-01-24 06:51:25 +0000
@@ -87,6 +87,7 @@
 lib/alloca.h
 lib/arg-nonnull.h
 lib/c++defs.h
+lib/dirent.h
 lib/execinfo.h
 lib/fcntl.h
 lib/getopt.h

=== modified file 'ChangeLog'
--- ChangeLog	2013-01-23 23:49:46 +0000
+++ ChangeLog	2013-01-24 08:13:22 +0000
@@ -1,3 +1,21 @@
+2013-01-24  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency.
+	On my host, this speeds up directory-files-and-attributes by a
+	factor of 3, when applied to Emacs's src directory.
+	These functions are standardized by POSIX and are common these
+	days; fall back on a (slower) gnulib implementation if the host
+	is too old to supply them.
+	* .bzrignore: Add lib/dirent.h.
+	* lib/Makefile.am (libgnu_a_SOURCES): Add openat-die.c, save-cwd.c.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+	* lib/dirent.in.h, lib/fdopendir.c, lib/fstatat.c, lib/openat-priv.h:
+	* lib/openat-proc.c, lib/openat.h, m4/dirent_h.m4, m4/fdopendir.m4:
+	* m4/fstatat.m4: New files, from gnulib.
+	* lib/openat-die.c, lib/save-cwd.c, lib/save-cwd.h: New files.
+	These last three are specific to Emacs and are not copied from gnulib.
+	They are simpler than the gnulib versions and are tuned for Emacs.
+
 2013-01-23  Giorgos Keramidas  <gkeramidas@gmail.com>  (tiny change)
 
 	* .bzrignore: add lib-src/blessmail.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2013-01-15 09:22:25 +0000
+++ admin/ChangeLog	2013-01-24 08:07:52 +0000
@@ -1,3 +1,10 @@
+2013-01-24  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency.
+	* merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat.
+	(GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h.
+	Avoid dup, open, opendir.
+
 2013-01-15  Dmitry Antipov  <dmantipov@yandex.ru>
 
 	* coccinelle/xsave.cocci: Semantic patch to adjust users of

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2013-01-02 16:13:04 +0000
+++ admin/merge-gnulib	2013-01-24 08:02:16 +0000
@@ -29,9 +29,9 @@
   alloca-opt c-ctype c-strcase
   careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
   dtoastr dtotimespec dup2 environ execinfo faccessat
-  fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday
+  fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
-  manywarnings mktime pselect pthread_sigmask putenv readlink
+  manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat
   sig2str socklen stat-time stdalign stdarg stdbool stdio
   strftime strtoimax strtoumax symlink sys_stat
   sys_time time timer-time timespec-add timespec-sub unsetenv utimens
@@ -39,10 +39,10 @@
 '
 
 GNULIB_TOOL_FLAGS='
-  --avoid=at-internal
+  --avoid=dup
   --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
   --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
-  --avoid=openat-die --avoid=openat-h
+  --avoid=open --avoid=openat-die --avoid=opendir
   --avoid=raise
   --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib

=== modified file 'lib/Makefile.am'
--- lib/Makefile.am	2012-08-02 10:39:47 +0000
+++ lib/Makefile.am	2013-01-24 06:51:25 +0000
@@ -8,3 +8,5 @@
 DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src
 
 include gnulib.mk
+
+libgnu_a_SOURCES += openat-die.c save-cwd.c

=== added file 'lib/dirent.in.h'
--- lib/dirent.in.h	1970-01-01 00:00:00 +0000
+++ lib/dirent.in.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,258 @@
+/* A GNU-like <dirent.h>.
+   Copyright (C) 2006-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _@GUARD_PREFIX@_DIRENT_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_DIRENT_H@
+# @INCLUDE_NEXT@ @NEXT_DIRENT_H@
+#endif
+
+#ifndef _@GUARD_PREFIX@_DIRENT_H
+#define _@GUARD_PREFIX@_DIRENT_H
+
+/* Get ino_t.  Needed on some systems, including glibc 2.8.  */
+#include <sys/types.h>
+
+#if !@HAVE_DIRENT_H@
+/* Define types DIR and 'struct dirent'.  */
+# if !GNULIB_defined_struct_dirent
+struct dirent
+{
+  char d_type;
+  char d_name[1];
+};
+/* Possible values for 'd_type'.  */
+#  define DT_UNKNOWN 0
+#  define DT_FIFO    1          /* FIFO */
+#  define DT_CHR     2          /* character device */
+#  define DT_DIR     4          /* directory */
+#  define DT_BLK     6          /* block device */
+#  define DT_REG     8          /* regular file */
+#  define DT_LNK    10          /* symbolic link */
+#  define DT_SOCK   12          /* socket */
+#  define DT_WHT    14          /* whiteout */
+typedef struct gl_directory DIR;
+#  define GNULIB_defined_struct_dirent 1
+# endif
+#endif
+
+/* The __attribute__ feature is available in gcc versions 2.5 and later.
+   The attribute __pure__ was added in gcc 2.96.  */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
+#else
+# define _GL_ATTRIBUTE_PURE /* empty */
+#endif
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here.  */
+
+/* The definition of _GL_ARG_NONNULL is copied here.  */
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* Declare overridden functions.  */
+
+#if @GNULIB_OPENDIR@
+# if @REPLACE_OPENDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef opendir
+#   define opendir rpl_opendir
+#  endif
+_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name));
+# else
+#  if !@HAVE_OPENDIR@
+_GL_FUNCDECL_SYS (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (opendir, DIR *, (const char *dir_name));
+# endif
+_GL_CXXALIASWARN (opendir);
+#elif defined GNULIB_POSIXCHECK
+# undef opendir
+# if HAVE_RAW_DECL_OPENDIR
+_GL_WARN_ON_USE (opendir, "opendir is not portable - "
+                 "use gnulib module opendir for portability");
+# endif
+#endif
+
+#if @GNULIB_READDIR@
+# if !@HAVE_READDIR@
+_GL_FUNCDECL_SYS (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (readdir, struct dirent *, (DIR *dirp));
+_GL_CXXALIASWARN (readdir);
+#elif defined GNULIB_POSIXCHECK
+# undef readdir
+# if HAVE_RAW_DECL_READDIR
+_GL_WARN_ON_USE (readdir, "readdir is not portable - "
+                 "use gnulib module readdir for portability");
+# endif
+#endif
+
+#if @GNULIB_REWINDDIR@
+# if !@HAVE_REWINDDIR@
+_GL_FUNCDECL_SYS (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (rewinddir, void, (DIR *dirp));
+_GL_CXXALIASWARN (rewinddir);
+#elif defined GNULIB_POSIXCHECK
+# undef rewinddir
+# if HAVE_RAW_DECL_REWINDDIR
+_GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - "
+                 "use gnulib module rewinddir for portability");
+# endif
+#endif
+
+#if @GNULIB_CLOSEDIR@
+# if @REPLACE_CLOSEDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef closedir
+#   define closedir rpl_closedir
+#  endif
+_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp));
+# else
+#  if !@HAVE_CLOSEDIR@
+_GL_FUNCDECL_SYS (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (closedir, int, (DIR *dirp));
+# endif
+_GL_CXXALIASWARN (closedir);
+#elif defined GNULIB_POSIXCHECK
+# undef closedir
+# if HAVE_RAW_DECL_CLOSEDIR
+_GL_WARN_ON_USE (closedir, "closedir is not portable - "
+                 "use gnulib module closedir for portability");
+# endif
+#endif
+
+#if @GNULIB_DIRFD@
+/* Return the file descriptor associated with the given directory stream,
+   or -1 if none exists.  */
+# if @REPLACE_DIRFD@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef dirfd
+#   define dirfd rpl_dirfd
+#  endif
+_GL_FUNCDECL_RPL (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (dirfd, int, (DIR *));
+# else
+#  if defined __cplusplus && defined GNULIB_NAMESPACE && defined dirfd
+    /* dirfd is defined as a macro and not as a function.
+       Turn it into a function and get rid of the macro.  */
+static inline int (dirfd) (DIR *dp) { return dirfd (dp); }
+#   undef dirfd
+#  endif
+#  if !(@HAVE_DECL_DIRFD@ || defined dirfd)
+_GL_FUNCDECL_SYS (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (dirfd, int, (DIR *));
+# endif
+_GL_CXXALIASWARN (dirfd);
+#elif defined GNULIB_POSIXCHECK
+# undef dirfd
+# if HAVE_RAW_DECL_DIRFD
+_GL_WARN_ON_USE (dirfd, "dirfd is unportable - "
+                 "use gnulib module dirfd for portability");
+# endif
+#endif
+
+#if @GNULIB_FDOPENDIR@
+/* Open a directory stream visiting the given directory file
+   descriptor.  Return NULL and set errno if fd is not visiting a
+   directory.  On success, this function consumes fd (it will be
+   implicitly closed either by this function or by a subsequent
+   closedir).  */
+# if @REPLACE_FDOPENDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fdopendir
+#   define fdopendir rpl_fdopendir
+#  endif
+_GL_FUNCDECL_RPL (fdopendir, DIR *, (int fd));
+_GL_CXXALIAS_RPL (fdopendir, DIR *, (int fd));
+# else
+#  if !@HAVE_FDOPENDIR@ || !@HAVE_DECL_FDOPENDIR@
+_GL_FUNCDECL_SYS (fdopendir, DIR *, (int fd));
+#  endif
+_GL_CXXALIAS_SYS (fdopendir, DIR *, (int fd));
+# endif
+_GL_CXXALIASWARN (fdopendir);
+#elif defined GNULIB_POSIXCHECK
+# undef fdopendir
+# if HAVE_RAW_DECL_FDOPENDIR
+_GL_WARN_ON_USE (fdopendir, "fdopendir is unportable - "
+                 "use gnulib module fdopendir for portability");
+# endif
+#endif
+
+#if @GNULIB_SCANDIR@
+/* Scan the directory DIR, calling FILTER on each directory entry.
+   Entries for which FILTER returns nonzero are individually malloc'd,
+   sorted using qsort with CMP, and collected in a malloc'd array in
+   *NAMELIST.  Returns the number of entries selected, or -1 on error.  */
+# if !@HAVE_SCANDIR@
+_GL_FUNCDECL_SYS (scandir, int,
+                  (const char *dir, struct dirent ***namelist,
+                   int (*filter) (const struct dirent *),
+                   int (*cmp) (const struct dirent **, const struct dirent **))
+                  _GL_ARG_NONNULL ((1, 2, 4)));
+# endif
+/* Need to cast, because on glibc systems, the fourth parameter is
+                        int (*cmp) (const void *, const void *).  */
+_GL_CXXALIAS_SYS_CAST (scandir, int,
+                       (const char *dir, struct dirent ***namelist,
+                        int (*filter) (const struct dirent *),
+                        int (*cmp) (const struct dirent **, const struct dirent **)));
+_GL_CXXALIASWARN (scandir);
+#elif defined GNULIB_POSIXCHECK
+# undef scandir
+# if HAVE_RAW_DECL_SCANDIR
+_GL_WARN_ON_USE (scandir, "scandir is unportable - "
+                 "use gnulib module scandir for portability");
+# endif
+#endif
+
+#if @GNULIB_ALPHASORT@
+/* Compare two 'struct dirent' entries alphabetically.  */
+# if !@HAVE_ALPHASORT@
+_GL_FUNCDECL_SYS (alphasort, int,
+                  (const struct dirent **, const struct dirent **)
+                  _GL_ATTRIBUTE_PURE
+                  _GL_ARG_NONNULL ((1, 2)));
+# endif
+/* Need to cast, because on glibc systems, the parameters are
+                       (const void *, const void *).  */
+_GL_CXXALIAS_SYS_CAST (alphasort, int,
+                       (const struct dirent **, const struct dirent **));
+_GL_CXXALIASWARN (alphasort);
+#elif defined GNULIB_POSIXCHECK
+# undef alphasort
+# if HAVE_RAW_DECL_ALPHASORT
+_GL_WARN_ON_USE (alphasort, "alphasort is unportable - "
+                 "use gnulib module alphasort for portability");
+# endif
+#endif
+
+
+#endif /* _@GUARD_PREFIX@_DIRENT_H */
+#endif /* _@GUARD_PREFIX@_DIRENT_H */

=== added file 'lib/fdopendir.c'
--- lib/fdopendir.c	1970-01-01 00:00:00 +0000
+++ lib/fdopendir.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,204 @@
+/* provide a replacement fdopendir function
+   Copyright (C) 2004-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#if !HAVE_FDOPENDIR
+
+# include "openat.h"
+# include "openat-priv.h"
+# include "save-cwd.h"
+
+# if GNULIB_DIRENT_SAFER
+#  include "dirent--.h"
+# endif
+
+# ifndef REPLACE_FCHDIR
+#  define REPLACE_FCHDIR 0
+# endif
+
+static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
+static DIR *fd_clone_opendir (int, struct saved_cwd const *);
+
+/* Replacement for POSIX fdopendir.
+
+   First, try to simulate it via opendir ("/proc/self/fd/...").  Failing
+   that, simulate it by using fchdir metadata, or by doing
+   save_cwd/fchdir/opendir(".")/restore_cwd.
+   If either the save_cwd or the restore_cwd fails (relatively unlikely),
+   then give a diagnostic and exit nonzero.
+
+   If successful, the resulting stream is based on FD in
+   implementations where streams are based on file descriptors and in
+   applications where no other thread or signal handler allocates or
+   frees file descriptors.  In other cases, consult dirfd on the result
+   to find out whether FD is still being used.
+
+   Otherwise, this function works just like POSIX fdopendir.
+
+   W A R N I N G:
+
+   Unlike other fd-related functions, this one places constraints on FD.
+   If this function returns successfully, FD is under control of the
+   dirent.h system, and the caller should not close or modify the state of
+   FD other than by the dirent.h functions.  */
+DIR *
+fdopendir (int fd)
+{
+  DIR *dir = fdopendir_with_dup (fd, -1, NULL);
+
+  if (! REPLACE_FCHDIR && ! dir)
+    {
+      int saved_errno = errno;
+      if (EXPECTED_ERRNO (saved_errno))
+        {
+          struct saved_cwd cwd;
+          if (save_cwd (&cwd) != 0)
+            openat_save_fail (errno);
+          dir = fdopendir_with_dup (fd, -1, &cwd);
+          saved_errno = errno;
+          free_cwd (&cwd);
+          errno = saved_errno;
+        }
+    }
+
+  return dir;
+}
+
+/* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
+   to be a dup of FD which is less than FD - 1 and which will be
+   closed by the caller and not otherwise used by the caller.  This
+   function makes sure that FD is closed and all file descriptors less
+   than FD are open, and then calls fd_clone_opendir on a dup of FD.
+   That way, barring race conditions, fd_clone_opendir returns a
+   stream whose file descriptor is FD.
+
+   If REPLACE_CHDIR or CWD is null, use opendir ("/proc/self/fd/...",
+   falling back on fchdir metadata.  Otherwise, CWD is a saved version
+   of the working directory; use fchdir/opendir(".")/restore_cwd(CWD).  */
+static DIR *
+fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
+{
+  int dupfd = dup (fd);
+  if (dupfd < 0 && errno == EMFILE)
+    dupfd = older_dupfd;
+  if (dupfd < 0)
+    return NULL;
+  else
+    {
+      DIR *dir;
+      int saved_errno;
+      if (dupfd < fd - 1 && dupfd != older_dupfd)
+        {
+          dir = fdopendir_with_dup (fd, dupfd, cwd);
+          saved_errno = errno;
+        }
+      else
+        {
+          close (fd);
+          dir = fd_clone_opendir (dupfd, cwd);
+          saved_errno = errno;
+          if (! dir)
+            {
+              int fd1 = dup (dupfd);
+              if (fd1 != fd)
+                openat_save_fail (fd1 < 0 ? errno : EBADF);
+            }
+        }
+
+      if (dupfd != older_dupfd)
+        close (dupfd);
+      errno = saved_errno;
+      return dir;
+    }
+}
+
+/* Like fdopendir, except the result controls a clone of FD.  It is
+   the caller's responsibility both to close FD and (if the result is
+   not null) to closedir the result.  */
+static DIR *
+fd_clone_opendir (int fd, struct saved_cwd const *cwd)
+{
+  if (REPLACE_FCHDIR || ! cwd)
+    {
+      DIR *dir = NULL;
+      int saved_errno = EOPNOTSUPP;
+      char buf[OPENAT_BUFFER_SIZE];
+      char *proc_file = openat_proc_name (buf, fd, ".");
+      if (proc_file)
+        {
+          dir = opendir (proc_file);
+          saved_errno = errno;
+          if (proc_file != buf)
+            free (proc_file);
+        }
+# if REPLACE_FCHDIR
+      if (! dir && EXPECTED_ERRNO (saved_errno))
+        {
+          char const *name = _gl_directory_name (fd);
+          return (name ? opendir (name) : NULL);
+        }
+# endif
+      errno = saved_errno;
+      return dir;
+    }
+  else
+    {
+      if (fchdir (fd) != 0)
+        return NULL;
+      else
+        {
+          DIR *dir = opendir (".");
+          int saved_errno = errno;
+          if (restore_cwd (cwd) != 0)
+            openat_restore_fail (errno);
+          errno = saved_errno;
+          return dir;
+        }
+    }
+}
+
+#else /* HAVE_FDOPENDIR */
+
+# include <errno.h>
+# include <sys/stat.h>
+
+# undef fdopendir
+
+/* Like fdopendir, but work around GNU/Hurd bug by validating FD.  */
+
+DIR *
+rpl_fdopendir (int fd)
+{
+  struct stat st;
+  if (fstat (fd, &st))
+    return NULL;
+  if (!S_ISDIR (st.st_mode))
+    {
+      errno = ENOTDIR;
+      return NULL;
+    }
+  return fdopendir (fd);
+}
+
+#endif /* HAVE_FDOPENDIR */

=== added file 'lib/fstatat.c'
--- lib/fstatat.c	1970-01-01 00:00:00 +0000
+++ lib/fstatat.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,135 @@
+/* Work around an fstatat bug on Solaris 9.
+
+   Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert and Jim Meyering.  */
+
+/* If the user's config.h happens to include <sys/stat.h>, let it include only
+   the system's <sys/stat.h> here, so that orig_fstatat doesn't recurse to
+   rpl_fstatat.  */
+#define __need_system_sys_stat_h
+#include <config.h>
+
+/* Get the original definition of fstatat.  It might be defined as a macro.  */
+#include <sys/types.h>
+#include <sys/stat.h>
+#undef __need_system_sys_stat_h
+
+#if HAVE_FSTATAT
+static int
+orig_fstatat (int fd, char const *filename, struct stat *buf, int flags)
+{
+  return fstatat (fd, filename, buf, flags);
+}
+#endif
+
+/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
+   eliminates this include because of the preliminary #include <sys/stat.h>
+   above.  */
+#include "sys/stat.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG
+
+# ifndef LSTAT_FOLLOWS_SLASHED_SYMLINK
+#  define LSTAT_FOLLOWS_SLASHED_SYMLINK 0
+# endif
+
+/* fstatat should always follow symbolic links that end in /, but on
+   Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
+   Likewise, trailing slash on a non-directory should be an error.
+   These are the same problems that lstat.c and stat.c address, so
+   solve it in a similar way.
+
+   AIX 7.1 fstatat (AT_FDCWD, ..., 0) always fails, which is a bug.
+   Work around this bug if FSTATAT_AT_FDCWD_0_BROKEN is nonzero.  */
+
+int
+rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
+{
+  int result = orig_fstatat (fd, file, st, flag);
+  size_t len;
+
+  if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0)
+    return result;
+  len = strlen (file);
+  if (flag & AT_SYMLINK_NOFOLLOW)
+    {
+      /* Fix lstat behavior.  */
+      if (file[len - 1] != '/' || S_ISDIR (st->st_mode))
+        return 0;
+      if (!S_ISLNK (st->st_mode))
+        {
+          errno = ENOTDIR;
+          return -1;
+        }
+      result = orig_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
+    }
+  /* Fix stat behavior.  */
+  if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
+    {
+      errno = ENOTDIR;
+      return -1;
+    }
+  return result;
+}
+
+#else /* ! (HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG) */
+
+/* On mingw, the gnulib <sys/stat.h> defines 'stat' as a function-like
+   macro; but using it in AT_FUNC_F2 causes compilation failure
+   because the preprocessor sees a use of a macro that requires two
+   arguments but is only given one.  Hence, we need an inline
+   forwarder to get past the preprocessor.  */
+static int
+stat_func (char const *name, struct stat *st)
+{
+  return stat (name, st);
+}
+
+/* Likewise, if there is no native 'lstat', then the gnulib
+   <sys/stat.h> defined it as stat, which also needs adjustment.  */
+# if !HAVE_LSTAT
+#  undef lstat
+#  define lstat stat_func
+# endif
+
+/* Replacement for Solaris' function by the same name.
+   <http://www.google.com/search?q=fstatat+site:docs.sun.com>
+   First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
+   Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
+   If either the save_cwd or the restore_cwd fails (relatively unlikely),
+   then give a diagnostic and exit nonzero.
+   Otherwise, this function works just like Solaris' fstatat.  */
+
+# define AT_FUNC_NAME fstatat
+# define AT_FUNC_F1 lstat
+# define AT_FUNC_F2 stat_func
+# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
+# define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
+# define AT_FUNC_POST_FILE_ARGS        , st
+# include "at-func.c"
+# undef AT_FUNC_NAME
+# undef AT_FUNC_F1
+# undef AT_FUNC_F2
+# undef AT_FUNC_USE_F1_COND
+# undef AT_FUNC_POST_FILE_PARAM_DECLS
+# undef AT_FUNC_POST_FILE_ARGS
+
+#endif /* !HAVE_FSTATAT */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2013-01-04 02:17:49 +0000
+++ lib/gnulib.mk	2013-01-24 08:02:16 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=at-internal --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=openat-h --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --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=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -64,6 +64,17 @@
 
 ## end   gnulib module allocator
 
+## begin gnulib module at-internal
+
+if gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b
+
+endif
+EXTRA_DIST += openat-priv.h openat-proc.c
+
+EXTRA_libgnu_a_SOURCES += openat-proc.c
+
+## end   gnulib module at-internal
+
 ## begin gnulib module c-ctype
 
 libgnu_a_SOURCES += c-ctype.h c-ctype.c
@@ -124,6 +135,54 @@
 
 ## end   gnulib module crypto/sha512
 
+## begin gnulib module dirent
+
+BUILT_SOURCES += dirent.h
+
+# We need the following in order to create <dirent.h> when the system
+# doesn't have one that works with the given compiler.
+dirent.h: dirent.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  sed -e 's|@''GUARD_PREFIX''@|GL|g' \
+	      -e 's|@''HAVE_DIRENT_H''@|$(HAVE_DIRENT_H)|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \
+	      -e 's/@''GNULIB_OPENDIR''@/$(GNULIB_OPENDIR)/g' \
+	      -e 's/@''GNULIB_READDIR''@/$(GNULIB_READDIR)/g' \
+	      -e 's/@''GNULIB_REWINDDIR''@/$(GNULIB_REWINDDIR)/g' \
+	      -e 's/@''GNULIB_CLOSEDIR''@/$(GNULIB_CLOSEDIR)/g' \
+	      -e 's/@''GNULIB_DIRFD''@/$(GNULIB_DIRFD)/g' \
+	      -e 's/@''GNULIB_FDOPENDIR''@/$(GNULIB_FDOPENDIR)/g' \
+	      -e 's/@''GNULIB_SCANDIR''@/$(GNULIB_SCANDIR)/g' \
+	      -e 's/@''GNULIB_ALPHASORT''@/$(GNULIB_ALPHASORT)/g' \
+	      -e 's/@''HAVE_OPENDIR''@/$(HAVE_OPENDIR)/g' \
+	      -e 's/@''HAVE_READDIR''@/$(HAVE_READDIR)/g' \
+	      -e 's/@''HAVE_REWINDDIR''@/$(HAVE_REWINDDIR)/g' \
+	      -e 's/@''HAVE_CLOSEDIR''@/$(HAVE_CLOSEDIR)/g' \
+	      -e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \
+	      -e 's|@''HAVE_DECL_FDOPENDIR''@|$(HAVE_DECL_FDOPENDIR)|g' \
+	      -e 's|@''HAVE_FDOPENDIR''@|$(HAVE_FDOPENDIR)|g' \
+	      -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \
+	      -e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \
+	      -e 's|@''REPLACE_OPENDIR''@|$(REPLACE_OPENDIR)|g' \
+	      -e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \
+	      -e 's|@''REPLACE_DIRFD''@|$(REPLACE_DIRFD)|g' \
+	      -e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \
+	      -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+	      -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+	      -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+	      < $(srcdir)/dirent.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+MOSTLYCLEANFILES += dirent.h dirent.h-t
+
+EXTRA_DIST += dirent.in.h
+
+## end   gnulib module dirent
+
 ## begin gnulib module dosname
 
 if gl_GNULIB_ENABLED_dosname
@@ -238,6 +297,15 @@
 
 ## end   gnulib module fcntl-h
 
+## begin gnulib module fdopendir
+
+
+EXTRA_DIST += fdopendir.c
+
+EXTRA_libgnu_a_SOURCES += fdopendir.c
+
+## end   gnulib module fdopendir
+
 ## begin gnulib module filemode
 
 libgnu_a_SOURCES += filemode.c
@@ -255,6 +323,15 @@
 
 ## end   gnulib module fpending
 
+## begin gnulib module fstatat
+
+
+EXTRA_DIST += at-func.c fstatat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c
+
+## end   gnulib module fstatat
+
 ## begin gnulib module getgroups
 
 if gl_GNULIB_ENABLED_getgroups
@@ -412,6 +489,15 @@
 
 ## end   gnulib module mktime
 
+## begin gnulib module openat-h
+
+if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
+
+endif
+EXTRA_DIST += openat.h
+
+## end   gnulib module openat-h
+
 ## begin gnulib module pathmax
 
 if gl_GNULIB_ENABLED_pathmax
@@ -457,6 +543,15 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module readlinkat
+
+
+EXTRA_DIST += at-func.c readlinkat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c readlinkat.c
+
+## end   gnulib module readlinkat
+
 ## begin gnulib module root-uid
 
 if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c

=== added file 'lib/openat-die.c'
--- lib/openat-die.c	1970-01-01 00:00:00 +0000
+++ lib/openat-die.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,6 @@
+/* Respond to a save- or restore-cwd failure.
+   This should never happen with Emacs.  */
+#include <config.h>
+#include "openat.h"
+void openat_save_fail (int errnum) { abort (); }
+void openat_restore_fail (int errnum) { abort (); }

=== added file 'lib/openat-priv.h'
--- lib/openat-priv.h	1970-01-01 00:00:00 +0000
+++ lib/openat-priv.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,64 @@
+/* Internals for openat-like functions.
+
+   Copyright (C) 2005-2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#ifndef _GL_HEADER_OPENAT_PRIV
+#define _GL_HEADER_OPENAT_PRIV
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/* Maximum number of bytes that it is safe to allocate as a single
+   array on the stack, and that is known as a compile-time constant.
+   The assumption is that we'll touch the array very quickly, or a
+   temporary very near the array, provoking an out-of-memory trap.  On
+   some operating systems, there is only one guard page for the stack,
+   and a page size can be as small as 4096 bytes.  Subtract 64 in the
+   hope that this will let the compiler touch a nearby temporary and
+   provoke a trap.  */
+#define SAFER_ALLOCA_MAX (4096 - 64)
+
+#define SAFER_ALLOCA(m) ((m) < SAFER_ALLOCA_MAX ? (m) : SAFER_ALLOCA_MAX)
+
+#if defined PATH_MAX
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (PATH_MAX)
+#elif defined _XOPEN_PATH_MAX
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (_XOPEN_PATH_MAX)
+#else
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (1024)
+#endif
+
+char *openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file);
+
+/* Trying to access a BUILD_PROC_NAME file will fail on systems without
+   /proc support, and even on systems *with* ProcFS support.  Return
+   nonzero if the failure may be legitimate, e.g., because /proc is not
+   readable, or the particular .../fd/N directory is not present.  */
+#define EXPECTED_ERRNO(Errno)                   \
+  ((Errno) == ENOTDIR || (Errno) == ENOENT      \
+   || (Errno) == EPERM || (Errno) == EACCES     \
+   || (Errno) == ENOSYS /* Solaris 8 */         \
+   || (Errno) == EOPNOTSUPP /* FreeBSD */)
+
+/* Wrapper function shared among linkat and renameat.  */
+int at_func2 (int fd1, char const *file1,
+              int fd2, char const *file2,
+              int (*func) (char const *file1, char const *file2));
+
+#endif /* _GL_HEADER_OPENAT_PRIV */

=== added file 'lib/openat-proc.c'
--- lib/openat-proc.c	1970-01-01 00:00:00 +0000
+++ lib/openat-proc.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,110 @@
+/* Create /proc/self/fd-related names for subfiles of open directories.
+
+   Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+#include <config.h>
+
+#include "openat-priv.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "intprops.h"
+
+/* The results of open() in this file are not used with fchdir,
+   and we do not leak fds to any single-threaded code that could use stdio,
+   therefore save some unnecessary work in fchdir.c.
+   FIXME - if the kernel ever adds support for multi-thread safety for
+   avoiding standard fds, then we should use open_safer.  */
+#undef open
+#undef close
+
+#define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/%s"
+
+#define PROC_SELF_FD_NAME_SIZE_BOUND(len) \
+  (sizeof PROC_SELF_FD_FORMAT - sizeof "%d%s" \
+   + INT_STRLEN_BOUND (int) + (len) + 1)
+
+
+/* Set BUF to the expansion of PROC_SELF_FD_FORMAT, using FD and FILE
+   respectively for %d and %s.  If successful, return BUF if the
+   result fits in BUF, dynamically allocated memory otherwise.  But
+   return NULL if /proc is not reliable, either because the operating
+   system support is lacking or because memory is low.  */
+char *
+openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
+{
+  static int proc_status = 0;
+
+  /* Make sure the caller gets ENOENT when appropriate.  */
+  if (!*file)
+    {
+      buf[0] = '\0';
+      return buf;
+    }
+
+  if (! proc_status)
+    {
+      /* Set PROC_STATUS to a positive value if /proc/self/fd is
+         reliable, and a negative value otherwise.  Solaris 10
+         /proc/self/fd mishandles "..", and any file name might expand
+         to ".." after symbolic link expansion, so avoid /proc/self/fd
+         if it mishandles "..".  Solaris 10 has openat, but this
+         problem is exhibited on code that built on Solaris 8 and
+         running on Solaris 10.  */
+
+      int proc_self_fd = open ("/proc/self/fd",
+                               O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
+      if (proc_self_fd < 0)
+        proc_status = -1;
+      else
+        {
+          /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
+             number of a file descriptor open on /proc/self/fd.  On Linux,
+             that name resolves to /proc/self/fd, which was opened above.
+             However, on Solaris, it may resolve to /proc/self/fd/fd, which
+             cannot exist, since all names in /proc/self/fd are numeric.  */
+          char dotdot_buf[PROC_SELF_FD_NAME_SIZE_BOUND (sizeof "../fd" - 1)];
+          sprintf (dotdot_buf, PROC_SELF_FD_FORMAT, proc_self_fd, "../fd");
+          proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
+          close (proc_self_fd);
+        }
+    }
+
+  if (proc_status < 0)
+    return NULL;
+  else
+    {
+      size_t bufsize = PROC_SELF_FD_NAME_SIZE_BOUND (strlen (file));
+      char *result = buf;
+      if (OPENAT_BUFFER_SIZE < bufsize)
+        {
+          result = malloc (bufsize);
+          if (! result)
+            return NULL;
+        }
+      sprintf (result, PROC_SELF_FD_FORMAT, fd, file);
+      return result;
+    }
+}

=== added file 'lib/openat.h'
--- lib/openat.h	1970-01-01 00:00:00 +0000
+++ lib/openat.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,120 @@
+/* provide a replacement openat function
+   Copyright (C) 2004-2006, 2008-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#ifndef _GL_HEADER_OPENAT
+#define _GL_HEADER_OPENAT
+
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+_GL_INLINE_HEADER_BEGIN
+
+#if !HAVE_OPENAT
+
+int openat_permissive (int fd, char const *file, int flags, mode_t mode,
+                       int *cwd_errno);
+bool openat_needs_fchdir (void);
+
+#else
+
+# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \
+    openat (Fd, File, Flags, Mode)
+# define openat_needs_fchdir() false
+
+#endif
+
+_Noreturn void openat_restore_fail (int);
+_Noreturn void openat_save_fail (int);
+
+/* Using these function names makes application code
+   slightly more readable than it would be with
+   fchownat (..., 0) or fchownat (..., AT_SYMLINK_NOFOLLOW).  */
+
+#if GNULIB_FCHOWNAT
+
+# ifndef FCHOWNAT_INLINE
+#  define FCHOWNAT_INLINE _GL_INLINE
+# endif
+
+FCHOWNAT_INLINE int
+chownat (int fd, char const *file, uid_t owner, gid_t group)
+{
+  return fchownat (fd, file, owner, group, 0);
+}
+
+FCHOWNAT_INLINE int
+lchownat (int fd, char const *file, uid_t owner, gid_t group)
+{
+  return fchownat (fd, file, owner, group, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+#if GNULIB_FCHMODAT
+
+# ifndef FCHMODAT_INLINE
+#  define FCHMODAT_INLINE _GL_INLINE
+# endif
+
+FCHMODAT_INLINE int
+chmodat (int fd, char const *file, mode_t mode)
+{
+  return fchmodat (fd, file, mode, 0);
+}
+
+FCHMODAT_INLINE int
+lchmodat (int fd, char const *file, mode_t mode)
+{
+  return fchmodat (fd, file, mode, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+#if GNULIB_STATAT
+
+# ifndef STATAT_INLINE
+#  define STATAT_INLINE _GL_INLINE
+# endif
+
+STATAT_INLINE int
+statat (int fd, char const *name, struct stat *st)
+{
+  return fstatat (fd, name, st, 0);
+}
+
+STATAT_INLINE int
+lstatat (int fd, char const *name, struct stat *st)
+{
+  return fstatat (fd, name, st, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+/* For now, there are no wrappers named laccessat or leuidaccessat,
+   since gnulib doesn't support faccessat(,AT_SYMLINK_NOFOLLOW) and
+   since access rights on symlinks are of limited utility.  Likewise,
+   wrappers are not provided for accessat or euidaccessat, so as to
+   avoid dragging in -lgen on some platforms.  */
+
+_GL_INLINE_HEADER_END
+
+#endif /* _GL_HEADER_OPENAT */

=== added file 'lib/readlinkat.c'
--- lib/readlinkat.c	1970-01-01 00:00:00 +0000
+++ lib/readlinkat.c	2013-01-24 08:02:16 +0000
@@ -0,0 +1,47 @@
+/* Read a symlink relative to an open directory.
+   Copyright (C) 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Eric Blake */
+
+#include <config.h>
+
+#include <unistd.h>
+
+/* Gnulib provides a readlink stub for mingw; use it for distinction
+   between EINVAL and ENOENT, rather than always failing with ENOSYS.  */
+
+/* POSIX 2008 says that unlike readlink, readlinkat returns 0 for
+   success instead of the buffer length.  But this would render
+   readlinkat worthless since readlink does not guarantee a
+   NUL-terminated buffer.  Assume this was a bug in POSIX.  */
+
+/* Read the contents of symlink FILE into buffer BUF of size LEN, in the
+   directory open on descriptor FD.  If possible, do it without changing
+   the working directory.  Otherwise, resort to using save_cwd/fchdir,
+   then readlink/restore_cwd.  If either the save_cwd or the restore_cwd
+   fails, then give a diagnostic and exit nonzero.  */
+
+#define AT_FUNC_NAME readlinkat
+#define AT_FUNC_F1 readlink
+#define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len
+#define AT_FUNC_POST_FILE_ARGS        , buf, len
+#define AT_FUNC_RESULT ssize_t
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+#undef AT_FUNC_RESULT

=== added file 'lib/save-cwd.c'
--- lib/save-cwd.c	1970-01-01 00:00:00 +0000
+++ lib/save-cwd.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,3 @@
+#include <config.h>
+#define SAVE_CWD_INLINE _GL_EXTERN_INLINE
+#include "save-cwd.h"

=== added file 'lib/save-cwd.h'
--- lib/save-cwd.h	1970-01-01 00:00:00 +0000
+++ lib/save-cwd.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,46 @@
+/* Do not save and restore the current working directory.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Gnulib needs to save and restore the current working directory to
+   fully emulate functions like fstatat.  But Emacs doesn't care what
+   the current working directory is; it always uses absolute file
+   names.  This module replaces the Gnulib module by omitting the code
+   that Emacs does not need.  */
+
+#ifndef SAVE_CWD_H
+#define SAVE_CWD_H 1
+
+_GL_INLINE_HEADER_BEGIN
+#ifndef SAVE_CWD_INLINE
+# define SAVE_CWD_INLINE _GL_INLINE
+#endif
+
+struct saved_cwd { int desc; };
+
+SAVE_CWD_INLINE int
+save_cwd (struct saved_cwd *cwd)
+{
+  cwd->desc = -1;
+  return 0;
+}
+
+SAVE_CWD_INLINE int restore_cwd (struct saved_cwd const *cwd) { return 0; }
+SAVE_CWD_INLINE void free_cwd (struct saved_cwd *cwd) { }
+
+_GL_INLINE_HEADER_END
+
+#endif

=== added file 'm4/dirent_h.m4'
--- m4/dirent_h.m4	1970-01-01 00:00:00 +0000
+++ m4/dirent_h.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,64 @@
+# dirent_h.m4 serial 16
+dnl Copyright (C) 2008-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Bruno Haible.
+
+AC_DEFUN([gl_DIRENT_H],
+[
+  dnl Use AC_REQUIRE here, so that the default behavior below is expanded
+  dnl once only, before all statements that occur in other macros.
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+
+  dnl <dirent.h> is always overridden, because of GNULIB_POSIXCHECK.
+  gl_CHECK_NEXT_HEADERS([dirent.h])
+  if test $ac_cv_header_dirent_h = yes; then
+    HAVE_DIRENT_H=1
+  else
+    HAVE_DIRENT_H=0
+  fi
+  AC_SUBST([HAVE_DIRENT_H])
+
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use.
+  gl_WARN_ON_USE_PREPARE([[#include <dirent.h>
+    ]], [alphasort closedir dirfd fdopendir opendir readdir rewinddir scandir])
+])
+
+AC_DEFUN([gl_DIRENT_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+AC_DEFUN([gl_DIRENT_H_DEFAULTS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR
+  GNULIB_OPENDIR=0;     AC_SUBST([GNULIB_OPENDIR])
+  GNULIB_READDIR=0;     AC_SUBST([GNULIB_READDIR])
+  GNULIB_REWINDDIR=0;   AC_SUBST([GNULIB_REWINDDIR])
+  GNULIB_CLOSEDIR=0;    AC_SUBST([GNULIB_CLOSEDIR])
+  GNULIB_DIRFD=0;       AC_SUBST([GNULIB_DIRFD])
+  GNULIB_FDOPENDIR=0;   AC_SUBST([GNULIB_FDOPENDIR])
+  GNULIB_SCANDIR=0;     AC_SUBST([GNULIB_SCANDIR])
+  GNULIB_ALPHASORT=0;   AC_SUBST([GNULIB_ALPHASORT])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_OPENDIR=1;       AC_SUBST([HAVE_OPENDIR])
+  HAVE_READDIR=1;       AC_SUBST([HAVE_READDIR])
+  HAVE_REWINDDIR=1;     AC_SUBST([HAVE_REWINDDIR])
+  HAVE_CLOSEDIR=1;      AC_SUBST([HAVE_CLOSEDIR])
+  HAVE_DECL_DIRFD=1;    AC_SUBST([HAVE_DECL_DIRFD])
+  HAVE_DECL_FDOPENDIR=1;AC_SUBST([HAVE_DECL_FDOPENDIR])
+  HAVE_FDOPENDIR=1;     AC_SUBST([HAVE_FDOPENDIR])
+  HAVE_SCANDIR=1;       AC_SUBST([HAVE_SCANDIR])
+  HAVE_ALPHASORT=1;     AC_SUBST([HAVE_ALPHASORT])
+  REPLACE_OPENDIR=0;    AC_SUBST([REPLACE_OPENDIR])
+  REPLACE_CLOSEDIR=0;   AC_SUBST([REPLACE_CLOSEDIR])
+  REPLACE_DIRFD=0;      AC_SUBST([REPLACE_DIRFD])
+  REPLACE_FDOPENDIR=0;  AC_SUBST([REPLACE_FDOPENDIR])
+])

=== added file 'm4/fdopendir.m4'
--- m4/fdopendir.m4	1970-01-01 00:00:00 +0000
+++ m4/fdopendir.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,61 @@
+# serial 10
+# See if we need to provide fdopendir.
+
+dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_FDOPENDIR],
+[
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  dnl FreeBSD 7.3 has the function, but failed to declare it.
+  AC_CHECK_DECLS([fdopendir], [], [HAVE_DECL_FDOPENDIR=0], [[
+#include <dirent.h>
+    ]])
+  AC_CHECK_FUNCS_ONCE([fdopendir])
+  if test $ac_cv_func_fdopendir = no; then
+    HAVE_FDOPENDIR=0
+  else
+    AC_CACHE_CHECK([whether fdopendir works],
+      [gl_cv_func_fdopendir_works],
+      [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#if !HAVE_DECL_FDOPENDIR
+extern
+# ifdef __cplusplus
+"C"
+# endif
+DIR *fdopendir (int);
+#endif
+]], [int result = 0;
+     int fd = open ("conftest.c", O_RDONLY);
+     if (fd < 0) result |= 1;
+     if (fdopendir (fd)) result |= 2;
+     if (close (fd)) result |= 4;
+     return result;])],
+         [gl_cv_func_fdopendir_works=yes],
+         [gl_cv_func_fdopendir_works=no],
+         [case "$host_os" in
+                    # Guess yes on glibc systems.
+            *-gnu*) gl_cv_func_fdopendir_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      gl_cv_func_fdopendir_works="guessing no" ;;
+          esac
+         ])])
+    case "$gl_cv_func_fdopendir_works" in
+      *yes) ;;
+      *)
+        REPLACE_FDOPENDIR=1
+        ;;
+    esac
+  fi
+])

=== added file 'm4/fstatat.m4'
--- m4/fstatat.m4	1970-01-01 00:00:00 +0000
+++ m4/fstatat.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,60 @@
+# fstatat.m4 serial 3
+dnl Copyright (C) 2004-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Jim Meyering.
+
+# If we have the fstatat function, and it has the bug (in AIX 7.1)
+# that it does not fill in st_size correctly, use the replacement function.
+AC_DEFUN([gl_FUNC_FSTATAT],
+[
+  AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CHECK_FUNCS_ONCE([fstatat])
+
+  if test $ac_cv_func_fstatat = no; then
+    HAVE_FSTATAT=0
+  else
+    dnl Test for an AIX 7.1 bug; see
+    dnl <http://lists.gnu.org/archive/html/bug-tar/2011-09/msg00015.html>.
+    AC_CACHE_CHECK([whether fstatat (..., 0) works],
+      [gl_cv_func_fstatat_zero_flag],
+      [AC_RUN_IFELSE(
+         [AC_LANG_SOURCE(
+            [[
+              #include <fcntl.h>
+              #include <sys/stat.h>
+              int
+              main (void)
+              {
+                struct stat a;
+                return fstatat (AT_FDCWD, ".", &a, 0) != 0;
+              }
+            ]])],
+         [gl_cv_func_fstatat_zero_flag=yes],
+         [gl_cv_func_fstatat_zero_flag=no],
+         [case "$host_os" in
+            aix*) gl_cv_func_fstatat_zero_flag="guessing no";;
+            *)    gl_cv_func_fstatat_zero_flag="guessing yes";;
+          esac
+         ])
+      ])
+
+    case $gl_cv_func_fstatat_zero_flag+$gl_cv_func_lstat_dereferences_slashed_symlink in
+    *yes+*yes) ;;
+    *) REPLACE_FSTATAT=1
+       case $gl_cv_func_fstatat_zero_flag in
+       *yes)
+         AC_DEFINE([HAVE_WORKING_FSTATAT_ZERO_FLAG], [1],
+           [Define to 1 if fstatat (..., 0) works.
+            For example, it does not work in AIX 7.1.])
+         ;;
+       esac
+       ;;
+    esac
+  fi
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2013-01-04 02:17:49 +0000
+++ m4/gnulib-comp.m4	2013-01-24 08:02:16 +0000
@@ -40,6 +40,7 @@
   AC_REQUIRE([gl_PROG_AR_RANLIB])
   # Code from module alloca-opt:
   # Code from module allocator:
+  # Code from module at-internal:
   # Code from module c-ctype:
   # Code from module c-strcase:
   # Code from module careadlinkat:
@@ -49,6 +50,7 @@
   # Code from module crypto/sha1:
   # Code from module crypto/sha256:
   # Code from module crypto/sha512:
+  # Code from module dirent:
   # Code from module dosname:
   # Code from module dtoastr:
   # Code from module dtotimespec:
@@ -61,8 +63,10 @@
   # Code from module extern-inline:
   # Code from module faccessat:
   # Code from module fcntl-h:
+  # Code from module fdopendir:
   # Code from module filemode:
   # Code from module fpending:
+  # Code from module fstatat:
   # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
@@ -82,11 +86,13 @@
   # Code from module mktime:
   # Code from module multiarch:
   # Code from module nocrash:
+  # Code from module openat-h:
   # Code from module pathmax:
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module putenv:
   # Code from module readlink:
+  # Code from module readlinkat:
   # Code from module root-uid:
   # Code from module sig2str:
   # Code from module signal-h:
@@ -159,6 +165,7 @@
   gl_SHA1
   gl_SHA256
   gl_SHA512
+  gl_DIRENT_H
   AC_REQUIRE([gl_C99_STRTOLD])
   gl_FUNC_DUP2
   if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then
@@ -178,12 +185,23 @@
   gl_MODULE_INDICATOR([faccessat])
   gl_UNISTD_MODULE_INDICATOR([faccessat])
   gl_FCNTL_H
+  gl_FUNC_FDOPENDIR
+  if test $HAVE_FDOPENDIR = 0 || test $REPLACE_FDOPENDIR = 1; then
+    AC_LIBOBJ([fdopendir])
+  fi
+  gl_DIRENT_MODULE_INDICATOR([fdopendir])
+  gl_MODULE_INDICATOR([fdopendir])
   gl_FILEMODE
   gl_FUNC_FPENDING
   if test $ac_cv_func___fpending = no; then
     AC_LIBOBJ([fpending])
     gl_PREREQ_FPENDING
   fi
+  gl_FUNC_FSTATAT
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    AC_LIBOBJ([fstatat])
+  fi
+  gl_SYS_STAT_MODULE_INDICATOR([fstatat])
   gl_GETLOADAVG
   if test $HAVE_GETLOADAVG = 0; then
     AC_LIBOBJ([getloadavg])
@@ -253,6 +271,11 @@
     gl_PREREQ_READLINK
   fi
   gl_UNISTD_MODULE_INDICATOR([readlink])
+  gl_FUNC_READLINKAT
+  if test $HAVE_READLINKAT = 0; then
+    AC_LIBOBJ([readlinkat])
+  fi
+  gl_UNISTD_MODULE_INDICATOR([readlinkat])
   gl_FUNC_SIG2STR
   if test $ac_cv_func_sig2str = no; then
     AC_LIBOBJ([sig2str])
@@ -311,11 +334,13 @@
   fi
   gl_STDLIB_MODULE_INDICATOR([unsetenv])
   gl_UTIMENS
+  gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false
   gl_gnulib_enabled_dosname=false
   gl_gnulib_enabled_euidaccess=false
   gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
   gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
+  gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
   gl_gnulib_enabled_stat=false
@@ -323,6 +348,13 @@
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
   gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
+  func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b ()
+  {
+    if ! $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then
+      AC_LIBOBJ([openat-proc])
+      gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=true
+    fi
+  }
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
@@ -385,6 +417,12 @@
       fi
     fi
   }
+  func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 ()
+  {
+    if ! $gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7; then
+      gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -456,11 +494,29 @@
     fi
   }
   if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FACCESSAT = 0; then
     func_gl_gnulib_m4code_dosname
   fi
   if test $HAVE_FACCESSAT = 0; then
     func_gl_gnulib_m4code_euidaccess
   fi
+  if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
+  if test $HAVE_FDOPENDIR = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -473,6 +529,15 @@
   if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then
     func_gl_gnulib_m4code_stat
   fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
   if { test $HAVE_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then
     func_gl_gnulib_m4code_strtoll
   fi
@@ -486,11 +551,13 @@
     func_gl_gnulib_m4code_verify
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
@@ -656,6 +723,7 @@
   lib/careadlinkat.h
   lib/close-stream.c
   lib/close-stream.h
+  lib/dirent.in.h
   lib/dosname.h
   lib/dtoastr.c
   lib/dtotimespec.c
@@ -665,10 +733,12 @@
   lib/execinfo.in.h
   lib/faccessat.c
   lib/fcntl.in.h
+  lib/fdopendir.c
   lib/filemode.c
   lib/filemode.h
   lib/fpending.c
   lib/fpending.h
+  lib/fstatat.c
   lib/ftoastr.c
   lib/ftoastr.h
   lib/getgroups.c
@@ -689,11 +759,15 @@
   lib/md5.h
   lib/mktime-internal.h
   lib/mktime.c
+  lib/openat-priv.h
+  lib/openat-proc.c
+  lib/openat.h
   lib/pathmax.h
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/putenv.c
   lib/readlink.c
+  lib/readlinkat.c
   lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
@@ -746,6 +820,7 @@
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/close-stream.m4
+  m4/dirent_h.m4
   m4/dup2.m4
   m4/environ.m4
   m4/euidaccess.m4
@@ -755,8 +830,10 @@
   m4/faccessat.m4
   m4/fcntl-o.m4
   m4/fcntl_h.m4
+  m4/fdopendir.m4
   m4/filemode.m4
   m4/fpending.m4
+  m4/fstatat.m4
   m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
@@ -780,6 +857,7 @@
   m4/pthread_sigmask.m4
   m4/putenv.m4
   m4/readlink.m4
+  m4/readlinkat.m4
   m4/setenv.m4
   m4/sha1.m4
   m4/sha256.m4

=== added file 'm4/readlinkat.m4'
--- m4/readlinkat.m4	1970-01-01 00:00:00 +0000
+++ m4/readlinkat.m4	2013-01-24 08:02:16 +0000
@@ -0,0 +1,19 @@
+# serial 3
+# See if we need to provide readlinkat replacement.
+
+dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_READLINKAT],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_CHECK_FUNCS_ONCE([readlinkat])
+  if test $ac_cv_func_readlinkat = no; then
+    HAVE_READLINKAT=0
+  fi
+])

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2013-01-24 05:56:31 +0000
+++ src/ChangeLog	2013-01-24 08:07:52 +0000
@@ -1,3 +1,31 @@
+2013-01-24  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency.
+	* conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove.
+	* dired.c: Include <fcntl.h>.
+	(open_directory): New function, which uses open and fdopendir
+	rather than opendir.
+	(directory_files_internal, file_name_completion): Use it.
+	(file_attributes): New function, with most of the old Ffile_attributes.
+	(directory_files_internal, Ffile_attributes): Use it.
+	(file_attributes, file_name_completion_stat): First arg is now fd,
+	not dir name.  All uses changed.  Use fstatat rather than lstat +
+	stat.
+	(file_attributes): Use emacs_readlinkat rather than Ffile_symlink_p.
+	* fileio.c: Include <allocator.h>, <careadlinkat.h>.
+	(emacs_readlinkat): New function, with much of the old
+	Ffile_symlink_p, but with an fd argument for speed.
+	It uses readlinkat rather than careadlinkatcwd, so that it
+	need not assume the working directory.
+	(Ffile_symlink_p): Use it.
+	* filelock.c (current_lock_owner): Use emacs_readlinkat
+	rather than emacs_readlink.
+	* lisp.h (emacs_readlinkat): New decl.
+	(READLINK_BUFSIZE, emacs_readlink): Remove.
+	* sysdep.c: Do not include <allocator.h>, <careadlinkat.h>.
+	(emacs_norealloc_allocator, emacs_readlink): Remove.
+	This stuff is moved to fileio.c.
+
 2013-01-24  Dmitry Antipov  <dmantipov@yandex.ru>
 
 	* nsfont.m (ns_escape_name, ns_unescape_name, ns_registry_to_script):

=== modified file 'src/conf_post.h'
--- src/conf_post.h	2013-01-02 16:13:04 +0000
+++ src/conf_post.h	2013-01-24 06:51:25 +0000
@@ -182,10 +182,6 @@
 #endif
 #endif
 
-/* Tell gnulib to omit support for openat-related functions having a
-   first argument other than AT_FDCWD.  */
-#define GNULIB_SUPPORT_ONLY_AT_FDCWD
-
 #include <string.h>
 #include <stdlib.h>
 

=== modified file 'src/dired.c'
--- src/dired.c	2013-01-17 06:29:40 +0000
+++ src/dired.c	2013-01-24 06:51:25 +0000
@@ -30,6 +30,7 @@
 #include <grp.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 #include <dirent.h>
@@ -54,6 +55,7 @@
 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
@@ -66,6 +68,35 @@
 #endif
 }
 
+static DIR *
+open_directory (char const *name, int *fdp)
+{
+  DIR *d;
+  int fd, opendir_errno;
+
+  block_input ();
+
+  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);
+    }
+
+  unblock_input ();
+
+  *fdp = fd;
+  errno = opendir_errno;
+  return d;
+}
+
 #ifdef WINDOWSNT
 Lisp_Object
 directory_files_internal_w32_unwind (Lisp_Object arg)
@@ -96,6 +127,7 @@
 			  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 +174,7 @@
   /* 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 +289,9 @@
 
 	  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 +432,7 @@
   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 *dp, struct stat *);
 static Lisp_Object Qdefault_directory;
 
 static Lisp_Object
@@ -422,6 +440,7 @@
 		      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 +477,7 @@
 
   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 +512,7 @@
 			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 +789,9 @@
 }
 
 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 +804,15 @@
   _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
@@ -886,18 +890,8 @@
 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 +907,22 @@
 	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 +932,7 @@
   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 +941,7 @@
   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);
 

=== modified file 'src/fileio.c'
--- src/fileio.c	2013-01-23 20:07:28 +0000
+++ src/fileio.c	2013-01-24 08:02:16 +0000
@@ -82,6 +82,8 @@
 #endif
 
 #include "systime.h"
+#include <allocator.h>
+#include <careadlinkat.h>
 #include <stat-time.h>
 
 #ifdef HPUX
@@ -2758,6 +2760,29 @@
   return Qnil;
 }
 \f
+/* Relative to directory FD, return the symbolic link value of FILENAME.
+   On failure, return nil.  */
+Lisp_Object
+emacs_readlinkat (int fd, char const *filename)
+{
+  static struct allocator const emacs_norealloc_allocator =
+    { xmalloc, NULL, xfree, memory_full };
+  Lisp_Object val;
+  char readlink_buf[1024];
+  char *buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf,
+			    &emacs_norealloc_allocator, readlinkat);
+  if (!buf)
+    return Qnil;
+
+  val = build_string (buf);
+  if (buf[0] == '/' && strchr (buf, ':'))
+    val = concat2 (build_string ("/:"), val);
+  if (buf != readlink_buf)
+    xfree (buf);
+  val = DECODE_FILE (val);
+  return val;
+}
+
 DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
        doc: /* Return non-nil if file FILENAME is the name of a symbolic link.
 The value is the link target, as a string.
@@ -2768,9 +2793,6 @@
   (Lisp_Object filename)
 {
   Lisp_Object handler;
-  char *buf;
-  Lisp_Object val;
-  char readlink_buf[READLINK_BUFSIZE];
 
   CHECK_STRING (filename);
   filename = Fexpand_file_name (filename, Qnil);
@@ -2783,17 +2805,7 @@
 
   filename = ENCODE_FILE (filename);
 
-  buf = emacs_readlink (SSDATA (filename), readlink_buf);
-  if (! buf)
-    return Qnil;
-
-  val = build_string (buf);
-  if (buf[0] == '/' && strchr (buf, ':'))
-    val = concat2 (build_string ("/:"), val);
-  if (buf != readlink_buf)
-    xfree (buf);
-  val = DECODE_FILE (val);
-  return val;
+  return emacs_readlinkat (AT_FDCWD, SSDATA (filename));
 }
 
 DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0,

=== modified file 'src/filelock.c'
--- src/filelock.c	2013-01-01 09:11:05 +0000
+++ src/filelock.c	2013-01-24 06:51:25 +0000
@@ -390,12 +390,14 @@
   lock_info_type local_owner;
   intmax_t n;
   char *at, *dot, *colon;
-  char readlink_buf[READLINK_BUFSIZE];
-  char *lfinfo = emacs_readlink (lfname, readlink_buf);
+  Lisp_Object lfinfo_object = emacs_readlinkat (AT_FDCWD, lfname);
+  char *lfinfo;
+  struct gcpro gcpro1;
 
   /* If nonexistent lock file, all is well; otherwise, got strange error. */
-  if (!lfinfo)
+  if (NILP (lfinfo_object))
     return errno == ENOENT ? 0 : -1;
+  lfinfo = SSDATA (lfinfo_object);
 
   /* Even if the caller doesn't want the owner info, we still have to
      read it to determine return value.  */
@@ -407,12 +409,9 @@
   at = strrchr (lfinfo, '@');
   dot = strrchr (lfinfo, '.');
   if (!at || !dot)
-    {
-      if (lfinfo != readlink_buf)
-	xfree (lfinfo);
-      return -1;
-    }
+    return -1;
   len = at - lfinfo;
+  GCPRO1 (lfinfo_object);
   owner->user = xmalloc (len + 1);
   memcpy (owner->user, lfinfo, len);
   owner->user[len] = 0;
@@ -445,8 +444,7 @@
   owner->host[len] = 0;
 
   /* We're done looking at the link info.  */
-  if (lfinfo != readlink_buf)
-    xfree (lfinfo);
+  UNGCPRO;
 
   /* On current host?  */
   if (STRINGP (Fsystem_name ())

=== modified file 'src/lisp.h'
--- src/lisp.h	2013-01-23 20:07:28 +0000
+++ src/lisp.h	2013-01-24 06:51:25 +0000
@@ -3295,6 +3295,7 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern bool internal_delete_file (Lisp_Object);
+extern Lisp_Object emacs_readlinkat (int, const char *);
 extern bool file_directory_p (const char *);
 extern bool file_accessible_directory_p (const char *);
 extern void init_fileio (void);
@@ -3567,8 +3568,6 @@
 extern int emacs_close (int);
 extern ptrdiff_t emacs_read (int, char *, ptrdiff_t);
 extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t);
-enum { READLINK_BUFSIZE = 1024 };
-extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]);
 
 extern void unlock_all_files (void);
 extern void lock_file (Lisp_Object);

=== modified file 'src/sysdep.c'
--- src/sysdep.c	2013-01-13 20:03:01 +0000
+++ src/sysdep.c	2013-01-24 06:51:25 +0000
@@ -30,9 +30,7 @@
 #include <limits.h>
 #include <unistd.h>
 
-#include <allocator.h>
 #include <c-ctype.h>
-#include <careadlinkat.h>
 #include <ignore-value.h>
 #include <utimens.h>
 
@@ -2247,22 +2245,6 @@
 
   return (bytes_written);
 }
-
-static struct allocator const emacs_norealloc_allocator =
-  { xmalloc, NULL, xfree, memory_full };
-
-/* Get the symbolic link value of FILENAME.  Return a pointer to a
-   NUL-terminated string.  If readlink fails, return NULL and set
-   errno.  If the value fits in INITIAL_BUF, return INITIAL_BUF.
-   Otherwise, allocate memory and return a pointer to that memory.  If
-   memory allocation fails, diagnose and fail without returning.  If
-   successful, store the length of the symbolic link into *LINKLEN.  */
-char *
-emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE])
-{
-  return careadlinkat (AT_FDCWD, filename, initial_buf, READLINK_BUFSIZE,
-		       &emacs_norealloc_allocator, careadlinkatcwd);
-}
 \f
 /* Return a struct timeval that is roughly equivalent to T.
    Use the least timeval not less than T.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-24  8:26 bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency Paul Eggert
@ 2013-01-24 17:28 ` Eli Zaretskii
  2013-01-28  8:34   ` Paul Eggert
  0 siblings, 1 reply; 9+ messages in thread
From: Eli Zaretskii @ 2013-01-24 17:28 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 13539

> Date: Thu, 24 Jan 2013 00:26:50 -0800
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: Eli Zaretskii <eliz@gnu.org>
> 
> Attached is a patch that can greatly improve the performance of
> directory-files-and-attributes, which I'd like to install
> into the trunk.  The key idea is to use fstatat
> instead of lstat; this reduces directory-reading
> from an O(ND) to an O(N) operation, where N is the number
> of directory entries and D is the length of
> the directory's name.

Is it really worth our while to spend energy on speeding up an API
that goes mostly unused in Emacs?  Its only heavy user is ls-lisp.el,
which is used on Windows to replace ls in Dired.  But the Windows
build will not be able to benefit from these changes anyway.  Just a
thought...

> This patch works on POSIXish hosts, but it'll no doubt
> need a few changes to the MS-Windows makefiles to compile
> fdopendir.c, fstatat.c, openat-die.c, save-cwd.c, openat-proc.c,
> or whatever subset of these is needed on MS-Windows.
> I'll CC: this to Eli to give him a heads-up.

I'm sorry, but this changeset is extremely unfriendly to the Windows
build.  It relies on the ability to open a directory, which is not
supported by 'open' on Windows; it uses the gnulib dirent replacement,
which is incompatible with the existing implementation in Emacs; it
uses sys/stat.h header and functions in ways that are incompatible
with their replacements in Emacs; and it requires to process many
header files with Sed, which was not required to build Emacs on
Windows until now.  All that to eventually fall back on opendir and
stat/lstat, since the *at functionality cannot be supported on Windows
anyway.  Fixing all this would require a lot of work for absolutely no
gain, which sounds like a waste of energy.

Instead, I suggest to use in dired.c functions that will accept both a
file name and a file descriptor, so that Posix hosts could use the
descriptor and ignore the file name, while on Windows Emacs will
ignore the descriptor and use the file name.  (This requires to retain
the code you suggest to delete that concatenates file names returned
by readdir with the parent directory; we could have that code
#ifdef'ed away for Posix hosts.)  That will still require some
adjustments in the w32 sources, but they will be 2 orders of magnitude
smaller and less complex, and will not hamper Posix hosts in any way.
The only downside will be somewhat more ugly code, but files like
dired.c already have quite a few #ifdef's, not surprisingly.

Thanks.





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-24 17:28 ` Eli Zaretskii
@ 2013-01-28  8:34   ` Paul Eggert
  2013-01-28 14:37     ` Eli Zaretskii
  0 siblings, 1 reply; 9+ messages in thread
From: Paul Eggert @ 2013-01-28  8:34 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 13539

[-- Attachment #1: Type: text/plain, Size: 1250 bytes --]

On 01/24/2013 09:28 AM, Eli Zaretskii wrote:

> Is it really worth our while to spend energy on speeding up an API
> that goes mostly unused in Emacs?  Its only heavy user is ls-lisp.el,

This patch also improves the performance of file-name-completion
-- perhaps you missed that part?  Anyway, on the POSIX side
it's a pretty simple patch to Emacs proper and is an obvious
performance win, and it'd be a shame if it couldn't be put in.

> I suggest to use in dired.c functions that will accept both a
> file name and a file descriptor,

Better than that, w32 can model the POSIX file descriptor by
using an int that represents the directory name.  The
attached patch does that.  It's a bit of a hack, but I don't
see why it wouldn't work.  If it doesn't work, we can fall
back on doing things the Gnulib way.  Gnulib uses 'int' to
emulate "open" directories on MS-Windows, and this works for
many other GNU applications.  It's probably cleaner to do it
that way in the long run, so this patch has a FIXME or two
about this.

In this patch, the MS-Windows port shouldn't need to worry
about the added source files; it should be able to ignore them all.
For example, it needn't worry about lib/dirent.in.h.

I haven't tested it on MS-Windows, though.

[-- Attachment #2: fdopendis.txt --]
[-- Type: text/plain, Size: 77407 bytes --]

=== modified file '.bzrignore'
--- .bzrignore	2013-01-23 23:49:46 +0000
+++ .bzrignore	2013-01-24 06:51:25 +0000
@@ -87,6 +87,7 @@
 lib/alloca.h
 lib/arg-nonnull.h
 lib/c++defs.h
+lib/dirent.h
 lib/execinfo.h
 lib/fcntl.h
 lib/getopt.h

=== modified file 'ChangeLog'
--- ChangeLog	2013-01-23 23:49:46 +0000
+++ ChangeLog	2013-01-28 02:51:25 +0000
@@ -1,3 +1,21 @@
+2013-01-28  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	On my host, this speeds up directory-files-and-attributes by a
+	factor of 3, when applied to Emacs's src directory.
+	These functions are standardized by POSIX and are common these
+	days; fall back on a (slower) gnulib implementation if the host
+	is too old to supply them.
+	* .bzrignore: Add lib/dirent.h.
+	* lib/Makefile.am (libgnu_a_SOURCES): Add openat-die.c, save-cwd.c.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+	* lib/dirent.in.h, lib/fdopendir.c, lib/fstatat.c, lib/openat-priv.h:
+	* lib/openat-proc.c, lib/openat.h, m4/dirent_h.m4, m4/fdopendir.m4:
+	* m4/fstatat.m4: New files, from gnulib.
+	* lib/openat-die.c, lib/save-cwd.c, lib/save-cwd.h: New files.
+	These last three are specific to Emacs and are not copied from gnulib.
+	They are simpler than the gnulib versions and are tuned for Emacs.
+
 2013-01-23  Giorgos Keramidas  <gkeramidas@gmail.com>  (tiny change)
 
 	* .bzrignore: add lib-src/blessmail.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2013-01-15 09:22:25 +0000
+++ admin/ChangeLog	2013-01-28 02:51:25 +0000
@@ -1,3 +1,10 @@
+2013-01-28  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	* merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat.
+	(GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h.
+	Avoid dup, open, opendir.
+
 2013-01-15  Dmitry Antipov  <dmantipov@yandex.ru>
 
 	* coccinelle/xsave.cocci: Semantic patch to adjust users of

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2013-01-02 16:13:04 +0000
+++ admin/merge-gnulib	2013-01-24 08:02:16 +0000
@@ -29,9 +29,9 @@
   alloca-opt c-ctype c-strcase
   careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
   dtoastr dtotimespec dup2 environ execinfo faccessat
-  fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday
+  fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
-  manywarnings mktime pselect pthread_sigmask putenv readlink
+  manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat
   sig2str socklen stat-time stdalign stdarg stdbool stdio
   strftime strtoimax strtoumax symlink sys_stat
   sys_time time timer-time timespec-add timespec-sub unsetenv utimens
@@ -39,10 +39,10 @@
 '
 
 GNULIB_TOOL_FLAGS='
-  --avoid=at-internal
+  --avoid=dup
   --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
   --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
-  --avoid=openat-die --avoid=openat-h
+  --avoid=open --avoid=openat-die --avoid=opendir
   --avoid=raise
   --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib

=== modified file 'lib/Makefile.am'
--- lib/Makefile.am	2012-08-02 10:39:47 +0000
+++ lib/Makefile.am	2013-01-24 06:51:25 +0000
@@ -8,3 +8,5 @@
 DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src
 
 include gnulib.mk
+
+libgnu_a_SOURCES += openat-die.c save-cwd.c

=== added file 'lib/dirent.in.h'
--- lib/dirent.in.h	1970-01-01 00:00:00 +0000
+++ lib/dirent.in.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,258 @@
+/* A GNU-like <dirent.h>.
+   Copyright (C) 2006-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _@GUARD_PREFIX@_DIRENT_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_DIRENT_H@
+# @INCLUDE_NEXT@ @NEXT_DIRENT_H@
+#endif
+
+#ifndef _@GUARD_PREFIX@_DIRENT_H
+#define _@GUARD_PREFIX@_DIRENT_H
+
+/* Get ino_t.  Needed on some systems, including glibc 2.8.  */
+#include <sys/types.h>
+
+#if !@HAVE_DIRENT_H@
+/* Define types DIR and 'struct dirent'.  */
+# if !GNULIB_defined_struct_dirent
+struct dirent
+{
+  char d_type;
+  char d_name[1];
+};
+/* Possible values for 'd_type'.  */
+#  define DT_UNKNOWN 0
+#  define DT_FIFO    1          /* FIFO */
+#  define DT_CHR     2          /* character device */
+#  define DT_DIR     4          /* directory */
+#  define DT_BLK     6          /* block device */
+#  define DT_REG     8          /* regular file */
+#  define DT_LNK    10          /* symbolic link */
+#  define DT_SOCK   12          /* socket */
+#  define DT_WHT    14          /* whiteout */
+typedef struct gl_directory DIR;
+#  define GNULIB_defined_struct_dirent 1
+# endif
+#endif
+
+/* The __attribute__ feature is available in gcc versions 2.5 and later.
+   The attribute __pure__ was added in gcc 2.96.  */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
+#else
+# define _GL_ATTRIBUTE_PURE /* empty */
+#endif
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here.  */
+
+/* The definition of _GL_ARG_NONNULL is copied here.  */
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* Declare overridden functions.  */
+
+#if @GNULIB_OPENDIR@
+# if @REPLACE_OPENDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef opendir
+#   define opendir rpl_opendir
+#  endif
+_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name));
+# else
+#  if !@HAVE_OPENDIR@
+_GL_FUNCDECL_SYS (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (opendir, DIR *, (const char *dir_name));
+# endif
+_GL_CXXALIASWARN (opendir);
+#elif defined GNULIB_POSIXCHECK
+# undef opendir
+# if HAVE_RAW_DECL_OPENDIR
+_GL_WARN_ON_USE (opendir, "opendir is not portable - "
+                 "use gnulib module opendir for portability");
+# endif
+#endif
+
+#if @GNULIB_READDIR@
+# if !@HAVE_READDIR@
+_GL_FUNCDECL_SYS (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (readdir, struct dirent *, (DIR *dirp));
+_GL_CXXALIASWARN (readdir);
+#elif defined GNULIB_POSIXCHECK
+# undef readdir
+# if HAVE_RAW_DECL_READDIR
+_GL_WARN_ON_USE (readdir, "readdir is not portable - "
+                 "use gnulib module readdir for portability");
+# endif
+#endif
+
+#if @GNULIB_REWINDDIR@
+# if !@HAVE_REWINDDIR@
+_GL_FUNCDECL_SYS (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (rewinddir, void, (DIR *dirp));
+_GL_CXXALIASWARN (rewinddir);
+#elif defined GNULIB_POSIXCHECK
+# undef rewinddir
+# if HAVE_RAW_DECL_REWINDDIR
+_GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - "
+                 "use gnulib module rewinddir for portability");
+# endif
+#endif
+
+#if @GNULIB_CLOSEDIR@
+# if @REPLACE_CLOSEDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef closedir
+#   define closedir rpl_closedir
+#  endif
+_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp));
+# else
+#  if !@HAVE_CLOSEDIR@
+_GL_FUNCDECL_SYS (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (closedir, int, (DIR *dirp));
+# endif
+_GL_CXXALIASWARN (closedir);
+#elif defined GNULIB_POSIXCHECK
+# undef closedir
+# if HAVE_RAW_DECL_CLOSEDIR
+_GL_WARN_ON_USE (closedir, "closedir is not portable - "
+                 "use gnulib module closedir for portability");
+# endif
+#endif
+
+#if @GNULIB_DIRFD@
+/* Return the file descriptor associated with the given directory stream,
+   or -1 if none exists.  */
+# if @REPLACE_DIRFD@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef dirfd
+#   define dirfd rpl_dirfd
+#  endif
+_GL_FUNCDECL_RPL (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (dirfd, int, (DIR *));
+# else
+#  if defined __cplusplus && defined GNULIB_NAMESPACE && defined dirfd
+    /* dirfd is defined as a macro and not as a function.
+       Turn it into a function and get rid of the macro.  */
+static inline int (dirfd) (DIR *dp) { return dirfd (dp); }
+#   undef dirfd
+#  endif
+#  if !(@HAVE_DECL_DIRFD@ || defined dirfd)
+_GL_FUNCDECL_SYS (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (dirfd, int, (DIR *));
+# endif
+_GL_CXXALIASWARN (dirfd);
+#elif defined GNULIB_POSIXCHECK
+# undef dirfd
+# if HAVE_RAW_DECL_DIRFD
+_GL_WARN_ON_USE (dirfd, "dirfd is unportable - "
+                 "use gnulib module dirfd for portability");
+# endif
+#endif
+
+#if @GNULIB_FDOPENDIR@
+/* Open a directory stream visiting the given directory file
+   descriptor.  Return NULL and set errno if fd is not visiting a
+   directory.  On success, this function consumes fd (it will be
+   implicitly closed either by this function or by a subsequent
+   closedir).  */
+# if @REPLACE_FDOPENDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fdopendir
+#   define fdopendir rpl_fdopendir
+#  endif
+_GL_FUNCDECL_RPL (fdopendir, DIR *, (int fd));
+_GL_CXXALIAS_RPL (fdopendir, DIR *, (int fd));
+# else
+#  if !@HAVE_FDOPENDIR@ || !@HAVE_DECL_FDOPENDIR@
+_GL_FUNCDECL_SYS (fdopendir, DIR *, (int fd));
+#  endif
+_GL_CXXALIAS_SYS (fdopendir, DIR *, (int fd));
+# endif
+_GL_CXXALIASWARN (fdopendir);
+#elif defined GNULIB_POSIXCHECK
+# undef fdopendir
+# if HAVE_RAW_DECL_FDOPENDIR
+_GL_WARN_ON_USE (fdopendir, "fdopendir is unportable - "
+                 "use gnulib module fdopendir for portability");
+# endif
+#endif
+
+#if @GNULIB_SCANDIR@
+/* Scan the directory DIR, calling FILTER on each directory entry.
+   Entries for which FILTER returns nonzero are individually malloc'd,
+   sorted using qsort with CMP, and collected in a malloc'd array in
+   *NAMELIST.  Returns the number of entries selected, or -1 on error.  */
+# if !@HAVE_SCANDIR@
+_GL_FUNCDECL_SYS (scandir, int,
+                  (const char *dir, struct dirent ***namelist,
+                   int (*filter) (const struct dirent *),
+                   int (*cmp) (const struct dirent **, const struct dirent **))
+                  _GL_ARG_NONNULL ((1, 2, 4)));
+# endif
+/* Need to cast, because on glibc systems, the fourth parameter is
+                        int (*cmp) (const void *, const void *).  */
+_GL_CXXALIAS_SYS_CAST (scandir, int,
+                       (const char *dir, struct dirent ***namelist,
+                        int (*filter) (const struct dirent *),
+                        int (*cmp) (const struct dirent **, const struct dirent **)));
+_GL_CXXALIASWARN (scandir);
+#elif defined GNULIB_POSIXCHECK
+# undef scandir
+# if HAVE_RAW_DECL_SCANDIR
+_GL_WARN_ON_USE (scandir, "scandir is unportable - "
+                 "use gnulib module scandir for portability");
+# endif
+#endif
+
+#if @GNULIB_ALPHASORT@
+/* Compare two 'struct dirent' entries alphabetically.  */
+# if !@HAVE_ALPHASORT@
+_GL_FUNCDECL_SYS (alphasort, int,
+                  (const struct dirent **, const struct dirent **)
+                  _GL_ATTRIBUTE_PURE
+                  _GL_ARG_NONNULL ((1, 2)));
+# endif
+/* Need to cast, because on glibc systems, the parameters are
+                       (const void *, const void *).  */
+_GL_CXXALIAS_SYS_CAST (alphasort, int,
+                       (const struct dirent **, const struct dirent **));
+_GL_CXXALIASWARN (alphasort);
+#elif defined GNULIB_POSIXCHECK
+# undef alphasort
+# if HAVE_RAW_DECL_ALPHASORT
+_GL_WARN_ON_USE (alphasort, "alphasort is unportable - "
+                 "use gnulib module alphasort for portability");
+# endif
+#endif
+
+
+#endif /* _@GUARD_PREFIX@_DIRENT_H */
+#endif /* _@GUARD_PREFIX@_DIRENT_H */

=== added file 'lib/fdopendir.c'
--- lib/fdopendir.c	1970-01-01 00:00:00 +0000
+++ lib/fdopendir.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,204 @@
+/* provide a replacement fdopendir function
+   Copyright (C) 2004-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#if !HAVE_FDOPENDIR
+
+# include "openat.h"
+# include "openat-priv.h"
+# include "save-cwd.h"
+
+# if GNULIB_DIRENT_SAFER
+#  include "dirent--.h"
+# endif
+
+# ifndef REPLACE_FCHDIR
+#  define REPLACE_FCHDIR 0
+# endif
+
+static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
+static DIR *fd_clone_opendir (int, struct saved_cwd const *);
+
+/* Replacement for POSIX fdopendir.
+
+   First, try to simulate it via opendir ("/proc/self/fd/...").  Failing
+   that, simulate it by using fchdir metadata, or by doing
+   save_cwd/fchdir/opendir(".")/restore_cwd.
+   If either the save_cwd or the restore_cwd fails (relatively unlikely),
+   then give a diagnostic and exit nonzero.
+
+   If successful, the resulting stream is based on FD in
+   implementations where streams are based on file descriptors and in
+   applications where no other thread or signal handler allocates or
+   frees file descriptors.  In other cases, consult dirfd on the result
+   to find out whether FD is still being used.
+
+   Otherwise, this function works just like POSIX fdopendir.
+
+   W A R N I N G:
+
+   Unlike other fd-related functions, this one places constraints on FD.
+   If this function returns successfully, FD is under control of the
+   dirent.h system, and the caller should not close or modify the state of
+   FD other than by the dirent.h functions.  */
+DIR *
+fdopendir (int fd)
+{
+  DIR *dir = fdopendir_with_dup (fd, -1, NULL);
+
+  if (! REPLACE_FCHDIR && ! dir)
+    {
+      int saved_errno = errno;
+      if (EXPECTED_ERRNO (saved_errno))
+        {
+          struct saved_cwd cwd;
+          if (save_cwd (&cwd) != 0)
+            openat_save_fail (errno);
+          dir = fdopendir_with_dup (fd, -1, &cwd);
+          saved_errno = errno;
+          free_cwd (&cwd);
+          errno = saved_errno;
+        }
+    }
+
+  return dir;
+}
+
+/* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
+   to be a dup of FD which is less than FD - 1 and which will be
+   closed by the caller and not otherwise used by the caller.  This
+   function makes sure that FD is closed and all file descriptors less
+   than FD are open, and then calls fd_clone_opendir on a dup of FD.
+   That way, barring race conditions, fd_clone_opendir returns a
+   stream whose file descriptor is FD.
+
+   If REPLACE_CHDIR or CWD is null, use opendir ("/proc/self/fd/...",
+   falling back on fchdir metadata.  Otherwise, CWD is a saved version
+   of the working directory; use fchdir/opendir(".")/restore_cwd(CWD).  */
+static DIR *
+fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
+{
+  int dupfd = dup (fd);
+  if (dupfd < 0 && errno == EMFILE)
+    dupfd = older_dupfd;
+  if (dupfd < 0)
+    return NULL;
+  else
+    {
+      DIR *dir;
+      int saved_errno;
+      if (dupfd < fd - 1 && dupfd != older_dupfd)
+        {
+          dir = fdopendir_with_dup (fd, dupfd, cwd);
+          saved_errno = errno;
+        }
+      else
+        {
+          close (fd);
+          dir = fd_clone_opendir (dupfd, cwd);
+          saved_errno = errno;
+          if (! dir)
+            {
+              int fd1 = dup (dupfd);
+              if (fd1 != fd)
+                openat_save_fail (fd1 < 0 ? errno : EBADF);
+            }
+        }
+
+      if (dupfd != older_dupfd)
+        close (dupfd);
+      errno = saved_errno;
+      return dir;
+    }
+}
+
+/* Like fdopendir, except the result controls a clone of FD.  It is
+   the caller's responsibility both to close FD and (if the result is
+   not null) to closedir the result.  */
+static DIR *
+fd_clone_opendir (int fd, struct saved_cwd const *cwd)
+{
+  if (REPLACE_FCHDIR || ! cwd)
+    {
+      DIR *dir = NULL;
+      int saved_errno = EOPNOTSUPP;
+      char buf[OPENAT_BUFFER_SIZE];
+      char *proc_file = openat_proc_name (buf, fd, ".");
+      if (proc_file)
+        {
+          dir = opendir (proc_file);
+          saved_errno = errno;
+          if (proc_file != buf)
+            free (proc_file);
+        }
+# if REPLACE_FCHDIR
+      if (! dir && EXPECTED_ERRNO (saved_errno))
+        {
+          char const *name = _gl_directory_name (fd);
+          return (name ? opendir (name) : NULL);
+        }
+# endif
+      errno = saved_errno;
+      return dir;
+    }
+  else
+    {
+      if (fchdir (fd) != 0)
+        return NULL;
+      else
+        {
+          DIR *dir = opendir (".");
+          int saved_errno = errno;
+          if (restore_cwd (cwd) != 0)
+            openat_restore_fail (errno);
+          errno = saved_errno;
+          return dir;
+        }
+    }
+}
+
+#else /* HAVE_FDOPENDIR */
+
+# include <errno.h>
+# include <sys/stat.h>
+
+# undef fdopendir
+
+/* Like fdopendir, but work around GNU/Hurd bug by validating FD.  */
+
+DIR *
+rpl_fdopendir (int fd)
+{
+  struct stat st;
+  if (fstat (fd, &st))
+    return NULL;
+  if (!S_ISDIR (st.st_mode))
+    {
+      errno = ENOTDIR;
+      return NULL;
+    }
+  return fdopendir (fd);
+}
+
+#endif /* HAVE_FDOPENDIR */

=== added file 'lib/fstatat.c'
--- lib/fstatat.c	1970-01-01 00:00:00 +0000
+++ lib/fstatat.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,135 @@
+/* Work around an fstatat bug on Solaris 9.
+
+   Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert and Jim Meyering.  */
+
+/* If the user's config.h happens to include <sys/stat.h>, let it include only
+   the system's <sys/stat.h> here, so that orig_fstatat doesn't recurse to
+   rpl_fstatat.  */
+#define __need_system_sys_stat_h
+#include <config.h>
+
+/* Get the original definition of fstatat.  It might be defined as a macro.  */
+#include <sys/types.h>
+#include <sys/stat.h>
+#undef __need_system_sys_stat_h
+
+#if HAVE_FSTATAT
+static int
+orig_fstatat (int fd, char const *filename, struct stat *buf, int flags)
+{
+  return fstatat (fd, filename, buf, flags);
+}
+#endif
+
+/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
+   eliminates this include because of the preliminary #include <sys/stat.h>
+   above.  */
+#include "sys/stat.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG
+
+# ifndef LSTAT_FOLLOWS_SLASHED_SYMLINK
+#  define LSTAT_FOLLOWS_SLASHED_SYMLINK 0
+# endif
+
+/* fstatat should always follow symbolic links that end in /, but on
+   Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
+   Likewise, trailing slash on a non-directory should be an error.
+   These are the same problems that lstat.c and stat.c address, so
+   solve it in a similar way.
+
+   AIX 7.1 fstatat (AT_FDCWD, ..., 0) always fails, which is a bug.
+   Work around this bug if FSTATAT_AT_FDCWD_0_BROKEN is nonzero.  */
+
+int
+rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
+{
+  int result = orig_fstatat (fd, file, st, flag);
+  size_t len;
+
+  if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0)
+    return result;
+  len = strlen (file);
+  if (flag & AT_SYMLINK_NOFOLLOW)
+    {
+      /* Fix lstat behavior.  */
+      if (file[len - 1] != '/' || S_ISDIR (st->st_mode))
+        return 0;
+      if (!S_ISLNK (st->st_mode))
+        {
+          errno = ENOTDIR;
+          return -1;
+        }
+      result = orig_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
+    }
+  /* Fix stat behavior.  */
+  if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
+    {
+      errno = ENOTDIR;
+      return -1;
+    }
+  return result;
+}
+
+#else /* ! (HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG) */
+
+/* On mingw, the gnulib <sys/stat.h> defines 'stat' as a function-like
+   macro; but using it in AT_FUNC_F2 causes compilation failure
+   because the preprocessor sees a use of a macro that requires two
+   arguments but is only given one.  Hence, we need an inline
+   forwarder to get past the preprocessor.  */
+static int
+stat_func (char const *name, struct stat *st)
+{
+  return stat (name, st);
+}
+
+/* Likewise, if there is no native 'lstat', then the gnulib
+   <sys/stat.h> defined it as stat, which also needs adjustment.  */
+# if !HAVE_LSTAT
+#  undef lstat
+#  define lstat stat_func
+# endif
+
+/* Replacement for Solaris' function by the same name.
+   <http://www.google.com/search?q=fstatat+site:docs.sun.com>
+   First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
+   Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
+   If either the save_cwd or the restore_cwd fails (relatively unlikely),
+   then give a diagnostic and exit nonzero.
+   Otherwise, this function works just like Solaris' fstatat.  */
+
+# define AT_FUNC_NAME fstatat
+# define AT_FUNC_F1 lstat
+# define AT_FUNC_F2 stat_func
+# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
+# define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
+# define AT_FUNC_POST_FILE_ARGS        , st
+# include "at-func.c"
+# undef AT_FUNC_NAME
+# undef AT_FUNC_F1
+# undef AT_FUNC_F2
+# undef AT_FUNC_USE_F1_COND
+# undef AT_FUNC_POST_FILE_PARAM_DECLS
+# undef AT_FUNC_POST_FILE_ARGS
+
+#endif /* !HAVE_FSTATAT */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2013-01-04 02:17:49 +0000
+++ lib/gnulib.mk	2013-01-24 08:02:16 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=at-internal --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=openat-h --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --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=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -64,6 +64,17 @@
 
 ## end   gnulib module allocator
 
+## begin gnulib module at-internal
+
+if gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b
+
+endif
+EXTRA_DIST += openat-priv.h openat-proc.c
+
+EXTRA_libgnu_a_SOURCES += openat-proc.c
+
+## end   gnulib module at-internal
+
 ## begin gnulib module c-ctype
 
 libgnu_a_SOURCES += c-ctype.h c-ctype.c
@@ -124,6 +135,54 @@
 
 ## end   gnulib module crypto/sha512
 
+## begin gnulib module dirent
+
+BUILT_SOURCES += dirent.h
+
+# We need the following in order to create <dirent.h> when the system
+# doesn't have one that works with the given compiler.
+dirent.h: dirent.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  sed -e 's|@''GUARD_PREFIX''@|GL|g' \
+	      -e 's|@''HAVE_DIRENT_H''@|$(HAVE_DIRENT_H)|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \
+	      -e 's/@''GNULIB_OPENDIR''@/$(GNULIB_OPENDIR)/g' \
+	      -e 's/@''GNULIB_READDIR''@/$(GNULIB_READDIR)/g' \
+	      -e 's/@''GNULIB_REWINDDIR''@/$(GNULIB_REWINDDIR)/g' \
+	      -e 's/@''GNULIB_CLOSEDIR''@/$(GNULIB_CLOSEDIR)/g' \
+	      -e 's/@''GNULIB_DIRFD''@/$(GNULIB_DIRFD)/g' \
+	      -e 's/@''GNULIB_FDOPENDIR''@/$(GNULIB_FDOPENDIR)/g' \
+	      -e 's/@''GNULIB_SCANDIR''@/$(GNULIB_SCANDIR)/g' \
+	      -e 's/@''GNULIB_ALPHASORT''@/$(GNULIB_ALPHASORT)/g' \
+	      -e 's/@''HAVE_OPENDIR''@/$(HAVE_OPENDIR)/g' \
+	      -e 's/@''HAVE_READDIR''@/$(HAVE_READDIR)/g' \
+	      -e 's/@''HAVE_REWINDDIR''@/$(HAVE_REWINDDIR)/g' \
+	      -e 's/@''HAVE_CLOSEDIR''@/$(HAVE_CLOSEDIR)/g' \
+	      -e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \
+	      -e 's|@''HAVE_DECL_FDOPENDIR''@|$(HAVE_DECL_FDOPENDIR)|g' \
+	      -e 's|@''HAVE_FDOPENDIR''@|$(HAVE_FDOPENDIR)|g' \
+	      -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \
+	      -e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \
+	      -e 's|@''REPLACE_OPENDIR''@|$(REPLACE_OPENDIR)|g' \
+	      -e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \
+	      -e 's|@''REPLACE_DIRFD''@|$(REPLACE_DIRFD)|g' \
+	      -e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \
+	      -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+	      -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+	      -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+	      < $(srcdir)/dirent.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+MOSTLYCLEANFILES += dirent.h dirent.h-t
+
+EXTRA_DIST += dirent.in.h
+
+## end   gnulib module dirent
+
 ## begin gnulib module dosname
 
 if gl_GNULIB_ENABLED_dosname
@@ -238,6 +297,15 @@
 
 ## end   gnulib module fcntl-h
 
+## begin gnulib module fdopendir
+
+
+EXTRA_DIST += fdopendir.c
+
+EXTRA_libgnu_a_SOURCES += fdopendir.c
+
+## end   gnulib module fdopendir
+
 ## begin gnulib module filemode
 
 libgnu_a_SOURCES += filemode.c
@@ -255,6 +323,15 @@
 
 ## end   gnulib module fpending
 
+## begin gnulib module fstatat
+
+
+EXTRA_DIST += at-func.c fstatat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c
+
+## end   gnulib module fstatat
+
 ## begin gnulib module getgroups
 
 if gl_GNULIB_ENABLED_getgroups
@@ -412,6 +489,15 @@
 
 ## end   gnulib module mktime
 
+## begin gnulib module openat-h
+
+if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
+
+endif
+EXTRA_DIST += openat.h
+
+## end   gnulib module openat-h
+
 ## begin gnulib module pathmax
 
 if gl_GNULIB_ENABLED_pathmax
@@ -457,6 +543,15 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module readlinkat
+
+
+EXTRA_DIST += at-func.c readlinkat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c readlinkat.c
+
+## end   gnulib module readlinkat
+
 ## begin gnulib module root-uid
 
 if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c

=== added file 'lib/openat-die.c'
--- lib/openat-die.c	1970-01-01 00:00:00 +0000
+++ lib/openat-die.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,6 @@
+/* Respond to a save- or restore-cwd failure.
+   This should never happen with Emacs.  */
+#include <config.h>
+#include "openat.h"
+void openat_save_fail (int errnum) { abort (); }
+void openat_restore_fail (int errnum) { abort (); }

=== added file 'lib/openat-priv.h'
--- lib/openat-priv.h	1970-01-01 00:00:00 +0000
+++ lib/openat-priv.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,64 @@
+/* Internals for openat-like functions.
+
+   Copyright (C) 2005-2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#ifndef _GL_HEADER_OPENAT_PRIV
+#define _GL_HEADER_OPENAT_PRIV
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/* Maximum number of bytes that it is safe to allocate as a single
+   array on the stack, and that is known as a compile-time constant.
+   The assumption is that we'll touch the array very quickly, or a
+   temporary very near the array, provoking an out-of-memory trap.  On
+   some operating systems, there is only one guard page for the stack,
+   and a page size can be as small as 4096 bytes.  Subtract 64 in the
+   hope that this will let the compiler touch a nearby temporary and
+   provoke a trap.  */
+#define SAFER_ALLOCA_MAX (4096 - 64)
+
+#define SAFER_ALLOCA(m) ((m) < SAFER_ALLOCA_MAX ? (m) : SAFER_ALLOCA_MAX)
+
+#if defined PATH_MAX
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (PATH_MAX)
+#elif defined _XOPEN_PATH_MAX
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (_XOPEN_PATH_MAX)
+#else
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (1024)
+#endif
+
+char *openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file);
+
+/* Trying to access a BUILD_PROC_NAME file will fail on systems without
+   /proc support, and even on systems *with* ProcFS support.  Return
+   nonzero if the failure may be legitimate, e.g., because /proc is not
+   readable, or the particular .../fd/N directory is not present.  */
+#define EXPECTED_ERRNO(Errno)                   \
+  ((Errno) == ENOTDIR || (Errno) == ENOENT      \
+   || (Errno) == EPERM || (Errno) == EACCES     \
+   || (Errno) == ENOSYS /* Solaris 8 */         \
+   || (Errno) == EOPNOTSUPP /* FreeBSD */)
+
+/* Wrapper function shared among linkat and renameat.  */
+int at_func2 (int fd1, char const *file1,
+              int fd2, char const *file2,
+              int (*func) (char const *file1, char const *file2));
+
+#endif /* _GL_HEADER_OPENAT_PRIV */

=== added file 'lib/openat-proc.c'
--- lib/openat-proc.c	1970-01-01 00:00:00 +0000
+++ lib/openat-proc.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,110 @@
+/* Create /proc/self/fd-related names for subfiles of open directories.
+
+   Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+#include <config.h>
+
+#include "openat-priv.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "intprops.h"
+
+/* The results of open() in this file are not used with fchdir,
+   and we do not leak fds to any single-threaded code that could use stdio,
+   therefore save some unnecessary work in fchdir.c.
+   FIXME - if the kernel ever adds support for multi-thread safety for
+   avoiding standard fds, then we should use open_safer.  */
+#undef open
+#undef close
+
+#define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/%s"
+
+#define PROC_SELF_FD_NAME_SIZE_BOUND(len) \
+  (sizeof PROC_SELF_FD_FORMAT - sizeof "%d%s" \
+   + INT_STRLEN_BOUND (int) + (len) + 1)
+
+
+/* Set BUF to the expansion of PROC_SELF_FD_FORMAT, using FD and FILE
+   respectively for %d and %s.  If successful, return BUF if the
+   result fits in BUF, dynamically allocated memory otherwise.  But
+   return NULL if /proc is not reliable, either because the operating
+   system support is lacking or because memory is low.  */
+char *
+openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
+{
+  static int proc_status = 0;
+
+  /* Make sure the caller gets ENOENT when appropriate.  */
+  if (!*file)
+    {
+      buf[0] = '\0';
+      return buf;
+    }
+
+  if (! proc_status)
+    {
+      /* Set PROC_STATUS to a positive value if /proc/self/fd is
+         reliable, and a negative value otherwise.  Solaris 10
+         /proc/self/fd mishandles "..", and any file name might expand
+         to ".." after symbolic link expansion, so avoid /proc/self/fd
+         if it mishandles "..".  Solaris 10 has openat, but this
+         problem is exhibited on code that built on Solaris 8 and
+         running on Solaris 10.  */
+
+      int proc_self_fd = open ("/proc/self/fd",
+                               O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
+      if (proc_self_fd < 0)
+        proc_status = -1;
+      else
+        {
+          /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
+             number of a file descriptor open on /proc/self/fd.  On Linux,
+             that name resolves to /proc/self/fd, which was opened above.
+             However, on Solaris, it may resolve to /proc/self/fd/fd, which
+             cannot exist, since all names in /proc/self/fd are numeric.  */
+          char dotdot_buf[PROC_SELF_FD_NAME_SIZE_BOUND (sizeof "../fd" - 1)];
+          sprintf (dotdot_buf, PROC_SELF_FD_FORMAT, proc_self_fd, "../fd");
+          proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
+          close (proc_self_fd);
+        }
+    }
+
+  if (proc_status < 0)
+    return NULL;
+  else
+    {
+      size_t bufsize = PROC_SELF_FD_NAME_SIZE_BOUND (strlen (file));
+      char *result = buf;
+      if (OPENAT_BUFFER_SIZE < bufsize)
+        {
+          result = malloc (bufsize);
+          if (! result)
+            return NULL;
+        }
+      sprintf (result, PROC_SELF_FD_FORMAT, fd, file);
+      return result;
+    }
+}

=== added file 'lib/openat.h'
--- lib/openat.h	1970-01-01 00:00:00 +0000
+++ lib/openat.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,120 @@
+/* provide a replacement openat function
+   Copyright (C) 2004-2006, 2008-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#ifndef _GL_HEADER_OPENAT
+#define _GL_HEADER_OPENAT
+
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+_GL_INLINE_HEADER_BEGIN
+
+#if !HAVE_OPENAT
+
+int openat_permissive (int fd, char const *file, int flags, mode_t mode,
+                       int *cwd_errno);
+bool openat_needs_fchdir (void);
+
+#else
+
+# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \
+    openat (Fd, File, Flags, Mode)
+# define openat_needs_fchdir() false
+
+#endif
+
+_Noreturn void openat_restore_fail (int);
+_Noreturn void openat_save_fail (int);
+
+/* Using these function names makes application code
+   slightly more readable than it would be with
+   fchownat (..., 0) or fchownat (..., AT_SYMLINK_NOFOLLOW).  */
+
+#if GNULIB_FCHOWNAT
+
+# ifndef FCHOWNAT_INLINE
+#  define FCHOWNAT_INLINE _GL_INLINE
+# endif
+
+FCHOWNAT_INLINE int
+chownat (int fd, char const *file, uid_t owner, gid_t group)
+{
+  return fchownat (fd, file, owner, group, 0);
+}
+
+FCHOWNAT_INLINE int
+lchownat (int fd, char const *file, uid_t owner, gid_t group)
+{
+  return fchownat (fd, file, owner, group, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+#if GNULIB_FCHMODAT
+
+# ifndef FCHMODAT_INLINE
+#  define FCHMODAT_INLINE _GL_INLINE
+# endif
+
+FCHMODAT_INLINE int
+chmodat (int fd, char const *file, mode_t mode)
+{
+  return fchmodat (fd, file, mode, 0);
+}
+
+FCHMODAT_INLINE int
+lchmodat (int fd, char const *file, mode_t mode)
+{
+  return fchmodat (fd, file, mode, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+#if GNULIB_STATAT
+
+# ifndef STATAT_INLINE
+#  define STATAT_INLINE _GL_INLINE
+# endif
+
+STATAT_INLINE int
+statat (int fd, char const *name, struct stat *st)
+{
+  return fstatat (fd, name, st, 0);
+}
+
+STATAT_INLINE int
+lstatat (int fd, char const *name, struct stat *st)
+{
+  return fstatat (fd, name, st, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+/* For now, there are no wrappers named laccessat or leuidaccessat,
+   since gnulib doesn't support faccessat(,AT_SYMLINK_NOFOLLOW) and
+   since access rights on symlinks are of limited utility.  Likewise,
+   wrappers are not provided for accessat or euidaccessat, so as to
+   avoid dragging in -lgen on some platforms.  */
+
+_GL_INLINE_HEADER_END
+
+#endif /* _GL_HEADER_OPENAT */

=== added file 'lib/readlinkat.c'
--- lib/readlinkat.c	1970-01-01 00:00:00 +0000
+++ lib/readlinkat.c	2013-01-24 08:02:16 +0000
@@ -0,0 +1,47 @@
+/* Read a symlink relative to an open directory.
+   Copyright (C) 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Eric Blake */
+
+#include <config.h>
+
+#include <unistd.h>
+
+/* Gnulib provides a readlink stub for mingw; use it for distinction
+   between EINVAL and ENOENT, rather than always failing with ENOSYS.  */
+
+/* POSIX 2008 says that unlike readlink, readlinkat returns 0 for
+   success instead of the buffer length.  But this would render
+   readlinkat worthless since readlink does not guarantee a
+   NUL-terminated buffer.  Assume this was a bug in POSIX.  */
+
+/* Read the contents of symlink FILE into buffer BUF of size LEN, in the
+   directory open on descriptor FD.  If possible, do it without changing
+   the working directory.  Otherwise, resort to using save_cwd/fchdir,
+   then readlink/restore_cwd.  If either the save_cwd or the restore_cwd
+   fails, then give a diagnostic and exit nonzero.  */
+
+#define AT_FUNC_NAME readlinkat
+#define AT_FUNC_F1 readlink
+#define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len
+#define AT_FUNC_POST_FILE_ARGS        , buf, len
+#define AT_FUNC_RESULT ssize_t
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+#undef AT_FUNC_RESULT

=== added file 'lib/save-cwd.c'
--- lib/save-cwd.c	1970-01-01 00:00:00 +0000
+++ lib/save-cwd.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,3 @@
+#include <config.h>
+#define SAVE_CWD_INLINE _GL_EXTERN_INLINE
+#include "save-cwd.h"

=== added file 'lib/save-cwd.h'
--- lib/save-cwd.h	1970-01-01 00:00:00 +0000
+++ lib/save-cwd.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,46 @@
+/* Do not save and restore the current working directory.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Gnulib needs to save and restore the current working directory to
+   fully emulate functions like fstatat.  But Emacs doesn't care what
+   the current working directory is; it always uses absolute file
+   names.  This module replaces the Gnulib module by omitting the code
+   that Emacs does not need.  */
+
+#ifndef SAVE_CWD_H
+#define SAVE_CWD_H 1
+
+_GL_INLINE_HEADER_BEGIN
+#ifndef SAVE_CWD_INLINE
+# define SAVE_CWD_INLINE _GL_INLINE
+#endif
+
+struct saved_cwd { int desc; };
+
+SAVE_CWD_INLINE int
+save_cwd (struct saved_cwd *cwd)
+{
+  cwd->desc = -1;
+  return 0;
+}
+
+SAVE_CWD_INLINE int restore_cwd (struct saved_cwd const *cwd) { return 0; }
+SAVE_CWD_INLINE void free_cwd (struct saved_cwd *cwd) { }
+
+_GL_INLINE_HEADER_END
+
+#endif

=== added file 'm4/dirent_h.m4'
--- m4/dirent_h.m4	1970-01-01 00:00:00 +0000
+++ m4/dirent_h.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,64 @@
+# dirent_h.m4 serial 16
+dnl Copyright (C) 2008-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Bruno Haible.
+
+AC_DEFUN([gl_DIRENT_H],
+[
+  dnl Use AC_REQUIRE here, so that the default behavior below is expanded
+  dnl once only, before all statements that occur in other macros.
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+
+  dnl <dirent.h> is always overridden, because of GNULIB_POSIXCHECK.
+  gl_CHECK_NEXT_HEADERS([dirent.h])
+  if test $ac_cv_header_dirent_h = yes; then
+    HAVE_DIRENT_H=1
+  else
+    HAVE_DIRENT_H=0
+  fi
+  AC_SUBST([HAVE_DIRENT_H])
+
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use.
+  gl_WARN_ON_USE_PREPARE([[#include <dirent.h>
+    ]], [alphasort closedir dirfd fdopendir opendir readdir rewinddir scandir])
+])
+
+AC_DEFUN([gl_DIRENT_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+AC_DEFUN([gl_DIRENT_H_DEFAULTS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR
+  GNULIB_OPENDIR=0;     AC_SUBST([GNULIB_OPENDIR])
+  GNULIB_READDIR=0;     AC_SUBST([GNULIB_READDIR])
+  GNULIB_REWINDDIR=0;   AC_SUBST([GNULIB_REWINDDIR])
+  GNULIB_CLOSEDIR=0;    AC_SUBST([GNULIB_CLOSEDIR])
+  GNULIB_DIRFD=0;       AC_SUBST([GNULIB_DIRFD])
+  GNULIB_FDOPENDIR=0;   AC_SUBST([GNULIB_FDOPENDIR])
+  GNULIB_SCANDIR=0;     AC_SUBST([GNULIB_SCANDIR])
+  GNULIB_ALPHASORT=0;   AC_SUBST([GNULIB_ALPHASORT])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_OPENDIR=1;       AC_SUBST([HAVE_OPENDIR])
+  HAVE_READDIR=1;       AC_SUBST([HAVE_READDIR])
+  HAVE_REWINDDIR=1;     AC_SUBST([HAVE_REWINDDIR])
+  HAVE_CLOSEDIR=1;      AC_SUBST([HAVE_CLOSEDIR])
+  HAVE_DECL_DIRFD=1;    AC_SUBST([HAVE_DECL_DIRFD])
+  HAVE_DECL_FDOPENDIR=1;AC_SUBST([HAVE_DECL_FDOPENDIR])
+  HAVE_FDOPENDIR=1;     AC_SUBST([HAVE_FDOPENDIR])
+  HAVE_SCANDIR=1;       AC_SUBST([HAVE_SCANDIR])
+  HAVE_ALPHASORT=1;     AC_SUBST([HAVE_ALPHASORT])
+  REPLACE_OPENDIR=0;    AC_SUBST([REPLACE_OPENDIR])
+  REPLACE_CLOSEDIR=0;   AC_SUBST([REPLACE_CLOSEDIR])
+  REPLACE_DIRFD=0;      AC_SUBST([REPLACE_DIRFD])
+  REPLACE_FDOPENDIR=0;  AC_SUBST([REPLACE_FDOPENDIR])
+])

=== added file 'm4/fdopendir.m4'
--- m4/fdopendir.m4	1970-01-01 00:00:00 +0000
+++ m4/fdopendir.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,61 @@
+# serial 10
+# See if we need to provide fdopendir.
+
+dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_FDOPENDIR],
+[
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  dnl FreeBSD 7.3 has the function, but failed to declare it.
+  AC_CHECK_DECLS([fdopendir], [], [HAVE_DECL_FDOPENDIR=0], [[
+#include <dirent.h>
+    ]])
+  AC_CHECK_FUNCS_ONCE([fdopendir])
+  if test $ac_cv_func_fdopendir = no; then
+    HAVE_FDOPENDIR=0
+  else
+    AC_CACHE_CHECK([whether fdopendir works],
+      [gl_cv_func_fdopendir_works],
+      [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#if !HAVE_DECL_FDOPENDIR
+extern
+# ifdef __cplusplus
+"C"
+# endif
+DIR *fdopendir (int);
+#endif
+]], [int result = 0;
+     int fd = open ("conftest.c", O_RDONLY);
+     if (fd < 0) result |= 1;
+     if (fdopendir (fd)) result |= 2;
+     if (close (fd)) result |= 4;
+     return result;])],
+         [gl_cv_func_fdopendir_works=yes],
+         [gl_cv_func_fdopendir_works=no],
+         [case "$host_os" in
+                    # Guess yes on glibc systems.
+            *-gnu*) gl_cv_func_fdopendir_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      gl_cv_func_fdopendir_works="guessing no" ;;
+          esac
+         ])])
+    case "$gl_cv_func_fdopendir_works" in
+      *yes) ;;
+      *)
+        REPLACE_FDOPENDIR=1
+        ;;
+    esac
+  fi
+])

=== added file 'm4/fstatat.m4'
--- m4/fstatat.m4	1970-01-01 00:00:00 +0000
+++ m4/fstatat.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,60 @@
+# fstatat.m4 serial 3
+dnl Copyright (C) 2004-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Jim Meyering.
+
+# If we have the fstatat function, and it has the bug (in AIX 7.1)
+# that it does not fill in st_size correctly, use the replacement function.
+AC_DEFUN([gl_FUNC_FSTATAT],
+[
+  AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CHECK_FUNCS_ONCE([fstatat])
+
+  if test $ac_cv_func_fstatat = no; then
+    HAVE_FSTATAT=0
+  else
+    dnl Test for an AIX 7.1 bug; see
+    dnl <http://lists.gnu.org/archive/html/bug-tar/2011-09/msg00015.html>.
+    AC_CACHE_CHECK([whether fstatat (..., 0) works],
+      [gl_cv_func_fstatat_zero_flag],
+      [AC_RUN_IFELSE(
+         [AC_LANG_SOURCE(
+            [[
+              #include <fcntl.h>
+              #include <sys/stat.h>
+              int
+              main (void)
+              {
+                struct stat a;
+                return fstatat (AT_FDCWD, ".", &a, 0) != 0;
+              }
+            ]])],
+         [gl_cv_func_fstatat_zero_flag=yes],
+         [gl_cv_func_fstatat_zero_flag=no],
+         [case "$host_os" in
+            aix*) gl_cv_func_fstatat_zero_flag="guessing no";;
+            *)    gl_cv_func_fstatat_zero_flag="guessing yes";;
+          esac
+         ])
+      ])
+
+    case $gl_cv_func_fstatat_zero_flag+$gl_cv_func_lstat_dereferences_slashed_symlink in
+    *yes+*yes) ;;
+    *) REPLACE_FSTATAT=1
+       case $gl_cv_func_fstatat_zero_flag in
+       *yes)
+         AC_DEFINE([HAVE_WORKING_FSTATAT_ZERO_FLAG], [1],
+           [Define to 1 if fstatat (..., 0) works.
+            For example, it does not work in AIX 7.1.])
+         ;;
+       esac
+       ;;
+    esac
+  fi
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2013-01-04 02:17:49 +0000
+++ m4/gnulib-comp.m4	2013-01-24 08:02:16 +0000
@@ -40,6 +40,7 @@
   AC_REQUIRE([gl_PROG_AR_RANLIB])
   # Code from module alloca-opt:
   # Code from module allocator:
+  # Code from module at-internal:
   # Code from module c-ctype:
   # Code from module c-strcase:
   # Code from module careadlinkat:
@@ -49,6 +50,7 @@
   # Code from module crypto/sha1:
   # Code from module crypto/sha256:
   # Code from module crypto/sha512:
+  # Code from module dirent:
   # Code from module dosname:
   # Code from module dtoastr:
   # Code from module dtotimespec:
@@ -61,8 +63,10 @@
   # Code from module extern-inline:
   # Code from module faccessat:
   # Code from module fcntl-h:
+  # Code from module fdopendir:
   # Code from module filemode:
   # Code from module fpending:
+  # Code from module fstatat:
   # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
@@ -82,11 +86,13 @@
   # Code from module mktime:
   # Code from module multiarch:
   # Code from module nocrash:
+  # Code from module openat-h:
   # Code from module pathmax:
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module putenv:
   # Code from module readlink:
+  # Code from module readlinkat:
   # Code from module root-uid:
   # Code from module sig2str:
   # Code from module signal-h:
@@ -159,6 +165,7 @@
   gl_SHA1
   gl_SHA256
   gl_SHA512
+  gl_DIRENT_H
   AC_REQUIRE([gl_C99_STRTOLD])
   gl_FUNC_DUP2
   if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then
@@ -178,12 +185,23 @@
   gl_MODULE_INDICATOR([faccessat])
   gl_UNISTD_MODULE_INDICATOR([faccessat])
   gl_FCNTL_H
+  gl_FUNC_FDOPENDIR
+  if test $HAVE_FDOPENDIR = 0 || test $REPLACE_FDOPENDIR = 1; then
+    AC_LIBOBJ([fdopendir])
+  fi
+  gl_DIRENT_MODULE_INDICATOR([fdopendir])
+  gl_MODULE_INDICATOR([fdopendir])
   gl_FILEMODE
   gl_FUNC_FPENDING
   if test $ac_cv_func___fpending = no; then
     AC_LIBOBJ([fpending])
     gl_PREREQ_FPENDING
   fi
+  gl_FUNC_FSTATAT
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    AC_LIBOBJ([fstatat])
+  fi
+  gl_SYS_STAT_MODULE_INDICATOR([fstatat])
   gl_GETLOADAVG
   if test $HAVE_GETLOADAVG = 0; then
     AC_LIBOBJ([getloadavg])
@@ -253,6 +271,11 @@
     gl_PREREQ_READLINK
   fi
   gl_UNISTD_MODULE_INDICATOR([readlink])
+  gl_FUNC_READLINKAT
+  if test $HAVE_READLINKAT = 0; then
+    AC_LIBOBJ([readlinkat])
+  fi
+  gl_UNISTD_MODULE_INDICATOR([readlinkat])
   gl_FUNC_SIG2STR
   if test $ac_cv_func_sig2str = no; then
     AC_LIBOBJ([sig2str])
@@ -311,11 +334,13 @@
   fi
   gl_STDLIB_MODULE_INDICATOR([unsetenv])
   gl_UTIMENS
+  gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false
   gl_gnulib_enabled_dosname=false
   gl_gnulib_enabled_euidaccess=false
   gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
   gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
+  gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
   gl_gnulib_enabled_stat=false
@@ -323,6 +348,13 @@
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
   gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
+  func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b ()
+  {
+    if ! $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then
+      AC_LIBOBJ([openat-proc])
+      gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=true
+    fi
+  }
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
@@ -385,6 +417,12 @@
       fi
     fi
   }
+  func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 ()
+  {
+    if ! $gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7; then
+      gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -456,11 +494,29 @@
     fi
   }
   if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FACCESSAT = 0; then
     func_gl_gnulib_m4code_dosname
   fi
   if test $HAVE_FACCESSAT = 0; then
     func_gl_gnulib_m4code_euidaccess
   fi
+  if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
+  if test $HAVE_FDOPENDIR = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -473,6 +529,15 @@
   if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then
     func_gl_gnulib_m4code_stat
   fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
   if { test $HAVE_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then
     func_gl_gnulib_m4code_strtoll
   fi
@@ -486,11 +551,13 @@
     func_gl_gnulib_m4code_verify
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
@@ -656,6 +723,7 @@
   lib/careadlinkat.h
   lib/close-stream.c
   lib/close-stream.h
+  lib/dirent.in.h
   lib/dosname.h
   lib/dtoastr.c
   lib/dtotimespec.c
@@ -665,10 +733,12 @@
   lib/execinfo.in.h
   lib/faccessat.c
   lib/fcntl.in.h
+  lib/fdopendir.c
   lib/filemode.c
   lib/filemode.h
   lib/fpending.c
   lib/fpending.h
+  lib/fstatat.c
   lib/ftoastr.c
   lib/ftoastr.h
   lib/getgroups.c
@@ -689,11 +759,15 @@
   lib/md5.h
   lib/mktime-internal.h
   lib/mktime.c
+  lib/openat-priv.h
+  lib/openat-proc.c
+  lib/openat.h
   lib/pathmax.h
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/putenv.c
   lib/readlink.c
+  lib/readlinkat.c
   lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
@@ -746,6 +820,7 @@
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/close-stream.m4
+  m4/dirent_h.m4
   m4/dup2.m4
   m4/environ.m4
   m4/euidaccess.m4
@@ -755,8 +830,10 @@
   m4/faccessat.m4
   m4/fcntl-o.m4
   m4/fcntl_h.m4
+  m4/fdopendir.m4
   m4/filemode.m4
   m4/fpending.m4
+  m4/fstatat.m4
   m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
@@ -780,6 +857,7 @@
   m4/pthread_sigmask.m4
   m4/putenv.m4
   m4/readlink.m4
+  m4/readlinkat.m4
   m4/setenv.m4
   m4/sha1.m4
   m4/sha256.m4

=== added file 'm4/readlinkat.m4'
--- m4/readlinkat.m4	1970-01-01 00:00:00 +0000
+++ m4/readlinkat.m4	2013-01-24 08:02:16 +0000
@@ -0,0 +1,19 @@
+# serial 3
+# See if we need to provide readlinkat replacement.
+
+dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_READLINKAT],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_CHECK_FUNCS_ONCE([readlinkat])
+  if test $ac_cv_func_readlinkat = no; then
+    HAVE_READLINKAT=0
+  fi
+])

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2013-01-11 09:33:54 +0000
+++ nt/ChangeLog	2013-01-28 08:15:12 +0000
@@ -1,3 +1,9 @@
+2013-01-28  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	* inc/sys/stat.h (fstatat):
+	* inc/unistd.h (readlinkat): New decls.
+
 2013-01-11  Eli Zaretskii  <eliz@gnu.org>
 
 	* inc/unistd.h (O_IGNORE_CTTY): Define, as it is unconditionally

=== modified file 'nt/inc/sys/stat.h'
--- nt/inc/sys/stat.h	2013-01-02 16:13:04 +0000
+++ nt/inc/sys/stat.h	2013-01-28 08:15:12 +0000
@@ -110,6 +110,7 @@
 _CRTIMP int __cdecl __MINGW_NOTHROW	chmod (const char*, int);
 _CRTIMP int __cdecl __MINGW_NOTHROW	stat (const char*, struct stat*);
 _CRTIMP int __cdecl __MINGW_NOTHROW	lstat (const char*, struct stat*);
+_CRTIMP int __cdecl __MINGW_NOTHROW	fstatat (int, char const *,
+						 struct stat *, int);
 
 #endif	/* INC_SYS_STAT_H_ */
-

=== modified file 'nt/inc/unistd.h'
--- nt/inc/unistd.h	2013-01-11 09:33:54 +0000
+++ nt/inc/unistd.h	2013-01-28 08:15:12 +0000
@@ -12,6 +12,7 @@
    and whose prototypes are usually found in unistd.h on POSIX
    platforms.  */
 extern ssize_t readlink (const char *, char *, size_t);
+extern ssize_t readlinkat (int, const char *, char *, size_t);
 extern int symlink (char const *, char const *);
 extern int setpgid (pid_t, pid_t);
 extern pid_t getpgrp (void);

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2013-01-28 05:47:51 +0000
+++ src/ChangeLog	2013-01-28 08:17:00 +0000
@@ -1,3 +1,34 @@
+2013-01-28  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	* conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove.
+	* dired.c: Include <fcntl.h>.
+	(open_directory): New function, which uses open and fdopendir
+	rather than opendir.  DOS_NT platforms still use opendir, though.
+	(directory_files_internal, file_name_completion): Use it.
+	(file_attributes): New function, with most of the old Ffile_attributes.
+	(directory_files_internal, Ffile_attributes): Use it.
+	(file_attributes, file_name_completion_stat): First arg is now fd,
+	not dir name.  All uses changed.  Use fstatat rather than lstat +
+	stat.
+	(file_attributes): Use emacs_readlinkat rather than Ffile_symlink_p.
+	* fileio.c: Include <allocator.h>, <careadlinkat.h>.
+	(emacs_readlinkat): New function, with much of the old
+	Ffile_symlink_p, but with an fd argument for speed.
+	It uses readlinkat rather than careadlinkatcwd, so that it
+	need not assume the working directory.
+	(Ffile_symlink_p): Use it.
+	* filelock.c (current_lock_owner): Use emacs_readlinkat
+	rather than emacs_readlink.
+	* lisp.h (emacs_readlinkat): New decl.
+	(READLINK_BUFSIZE, emacs_readlink): Remove.
+	* sysdep.c: Do not include <allocator.h>, <careadlinkat.h>.
+	(emacs_norealloc_allocator, emacs_readlink): Remove.
+	This stuff is moved to fileio.c.
+	* w32.c (fstatat, readlinkat): New functions.
+	(careadlinkat): Don't check that fd == AT_FDCWD.
+	(careadlinkatcwd): Remove; no longer needed.
+
 2013-01-28  Dmitry Antipov  <dmantipov@yandex.ru>
 
 	Remove obsolete redisplay code.  See the discussion at

=== modified file 'src/conf_post.h'
--- src/conf_post.h	2013-01-02 16:13:04 +0000
+++ src/conf_post.h	2013-01-24 06:51:25 +0000
@@ -182,10 +182,6 @@
 #endif
 #endif
 
-/* Tell gnulib to omit support for openat-related functions having a
-   first argument other than AT_FDCWD.  */
-#define GNULIB_SUPPORT_ONLY_AT_FDCWD
-
 #include <string.h>
 #include <stdlib.h>
 

=== modified file 'src/dired.c'
--- src/dired.c	2013-01-17 06:29:40 +0000
+++ src/dired.c	2013-01-28 08:15:12 +0000
@@ -30,6 +30,7 @@
 #include <grp.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 #include <dirent.h>
@@ -54,6 +55,7 @@
 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
@@ -66,6 +68,43 @@
 #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, so emulate an open directory with
+     its name, cast to a pointer.  This is good enough for Emacs.  */
+  fd = (int) name;
+  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 +135,7 @@
 			  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 +182,7 @@
   /* 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 +297,9 @@
 
 	  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 +440,7 @@
   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 +448,7 @@
 		      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 +485,7 @@
 
   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 +520,7 @@
 			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 +797,9 @@
 }
 
 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 +812,15 @@
   _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
@@ -886,18 +898,8 @@
 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 +915,22 @@
 	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 +940,7 @@
   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 +949,7 @@
   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);
 

=== modified file 'src/fileio.c'
--- src/fileio.c	2013-01-23 20:07:28 +0000
+++ src/fileio.c	2013-01-24 08:02:16 +0000
@@ -82,6 +82,8 @@
 #endif
 
 #include "systime.h"
+#include <allocator.h>
+#include <careadlinkat.h>
 #include <stat-time.h>
 
 #ifdef HPUX
@@ -2758,6 +2760,29 @@
   return Qnil;
 }
 \f
+/* Relative to directory FD, return the symbolic link value of FILENAME.
+   On failure, return nil.  */
+Lisp_Object
+emacs_readlinkat (int fd, char const *filename)
+{
+  static struct allocator const emacs_norealloc_allocator =
+    { xmalloc, NULL, xfree, memory_full };
+  Lisp_Object val;
+  char readlink_buf[1024];
+  char *buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf,
+			    &emacs_norealloc_allocator, readlinkat);
+  if (!buf)
+    return Qnil;
+
+  val = build_string (buf);
+  if (buf[0] == '/' && strchr (buf, ':'))
+    val = concat2 (build_string ("/:"), val);
+  if (buf != readlink_buf)
+    xfree (buf);
+  val = DECODE_FILE (val);
+  return val;
+}
+
 DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
        doc: /* Return non-nil if file FILENAME is the name of a symbolic link.
 The value is the link target, as a string.
@@ -2768,9 +2793,6 @@
   (Lisp_Object filename)
 {
   Lisp_Object handler;
-  char *buf;
-  Lisp_Object val;
-  char readlink_buf[READLINK_BUFSIZE];
 
   CHECK_STRING (filename);
   filename = Fexpand_file_name (filename, Qnil);
@@ -2783,17 +2805,7 @@
 
   filename = ENCODE_FILE (filename);
 
-  buf = emacs_readlink (SSDATA (filename), readlink_buf);
-  if (! buf)
-    return Qnil;
-
-  val = build_string (buf);
-  if (buf[0] == '/' && strchr (buf, ':'))
-    val = concat2 (build_string ("/:"), val);
-  if (buf != readlink_buf)
-    xfree (buf);
-  val = DECODE_FILE (val);
-  return val;
+  return emacs_readlinkat (AT_FDCWD, SSDATA (filename));
 }
 
 DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0,

=== modified file 'src/filelock.c'
--- src/filelock.c	2013-01-01 09:11:05 +0000
+++ src/filelock.c	2013-01-24 06:51:25 +0000
@@ -390,12 +390,14 @@
   lock_info_type local_owner;
   intmax_t n;
   char *at, *dot, *colon;
-  char readlink_buf[READLINK_BUFSIZE];
-  char *lfinfo = emacs_readlink (lfname, readlink_buf);
+  Lisp_Object lfinfo_object = emacs_readlinkat (AT_FDCWD, lfname);
+  char *lfinfo;
+  struct gcpro gcpro1;
 
   /* If nonexistent lock file, all is well; otherwise, got strange error. */
-  if (!lfinfo)
+  if (NILP (lfinfo_object))
     return errno == ENOENT ? 0 : -1;
+  lfinfo = SSDATA (lfinfo_object);
 
   /* Even if the caller doesn't want the owner info, we still have to
      read it to determine return value.  */
@@ -407,12 +409,9 @@
   at = strrchr (lfinfo, '@');
   dot = strrchr (lfinfo, '.');
   if (!at || !dot)
-    {
-      if (lfinfo != readlink_buf)
-	xfree (lfinfo);
-      return -1;
-    }
+    return -1;
   len = at - lfinfo;
+  GCPRO1 (lfinfo_object);
   owner->user = xmalloc (len + 1);
   memcpy (owner->user, lfinfo, len);
   owner->user[len] = 0;
@@ -445,8 +444,7 @@
   owner->host[len] = 0;
 
   /* We're done looking at the link info.  */
-  if (lfinfo != readlink_buf)
-    xfree (lfinfo);
+  UNGCPRO;
 
   /* On current host?  */
   if (STRINGP (Fsystem_name ())

=== modified file 'src/lisp.h'
--- src/lisp.h	2013-01-25 17:13:31 +0000
+++ src/lisp.h	2013-01-28 02:51:25 +0000
@@ -3294,6 +3294,7 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern bool internal_delete_file (Lisp_Object);
+extern Lisp_Object emacs_readlinkat (int, const char *);
 extern bool file_directory_p (const char *);
 extern bool file_accessible_directory_p (const char *);
 extern void init_fileio (void);
@@ -3566,8 +3567,6 @@
 extern int emacs_close (int);
 extern ptrdiff_t emacs_read (int, char *, ptrdiff_t);
 extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t);
-enum { READLINK_BUFSIZE = 1024 };
-extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]);
 
 extern void unlock_all_files (void);
 extern void lock_file (Lisp_Object);

=== modified file 'src/sysdep.c'
--- src/sysdep.c	2013-01-13 20:03:01 +0000
+++ src/sysdep.c	2013-01-24 06:51:25 +0000
@@ -30,9 +30,7 @@
 #include <limits.h>
 #include <unistd.h>
 
-#include <allocator.h>
 #include <c-ctype.h>
-#include <careadlinkat.h>
 #include <ignore-value.h>
 #include <utimens.h>
 
@@ -2247,22 +2245,6 @@
 
   return (bytes_written);
 }
-
-static struct allocator const emacs_norealloc_allocator =
-  { xmalloc, NULL, xfree, memory_full };
-
-/* Get the symbolic link value of FILENAME.  Return a pointer to a
-   NUL-terminated string.  If readlink fails, return NULL and set
-   errno.  If the value fits in INITIAL_BUF, return INITIAL_BUF.
-   Otherwise, allocate memory and return a pointer to that memory.  If
-   memory allocation fails, diagnose and fail without returning.  If
-   successful, store the length of the symbolic link into *LINKLEN.  */
-char *
-emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE])
-{
-  return careadlinkat (AT_FDCWD, filename, initial_buf, READLINK_BUFSIZE,
-		       &emacs_norealloc_allocator, careadlinkatcwd);
-}
 \f
 /* Return a struct timeval that is roughly equivalent to T.
    Use the least timeval not less than T.

=== modified file 'src/w32.c'
--- src/w32.c	2013-01-25 14:38:21 +0000
+++ src/w32.c	2013-01-28 08:15:12 +0000
@@ -4194,6 +4194,29 @@
   return stat_worker (path, buf, 0);
 }
 
+int
+fstatat (int fd, char const *name, struct stat *st, int flags)
+{
+  /* Rely on a hack: open directories are modeled as their names, cast
+     to int.  This is good enough for the current usage in Emacs, but
+     is fragile.
+
+     FIXME: Add proper support for fdopendir, fstatatat, readlinkat.
+     Gnulib does this and can serve as a model.  */
+  char fullname[MAX_PATH];
+  if (fd != AT_FDCWD)
+    {
+      if (_snprintf (fullname, sizeof fullname, "%s/%s", (char *) fd, name)
+	  < 0)
+	{
+	  errno = ENAMETOOLONG;
+	  return -1;
+	}
+      name = fullname;
+    }
+  return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
+}
+
 /* Provide fstat and utime as well as stat for consistent handling of
    file timestamps. */
 int
@@ -4697,6 +4720,28 @@
   return retval;
 }
 
+ssize_t
+readlinkat (int fd, char const *name, char *buffer,
+	    size_t buffer_size)
+{
+  /* Rely on a hack: open directories are modeled as their names cast
+     to int, as in fstatat.  FIXME: Add proper support for readlinkat.  */
+  char fullname[MAX_PATH];
+
+  if (fd != AT_FDCWD)
+    {
+      if (_snprintf (fullname, sizeof fullname, "%s/%s", (char *) fd, name)
+	  < 0)
+	{
+	  errno = ENAMETOOLONG;
+	  return -1;
+	}
+      name = fullname;
+    }
+
+  return readlink (name, buffer, buffer_size);
+}
+
 /* If FILE is a symlink, return its target (stored in a static
    buffer); otherwise return FILE.
 
@@ -5021,12 +5066,6 @@
   char linkname[MAX_PATH];
   ssize_t link_size;
 
-  if (fd != AT_FDCWD)
-    {
-      errno = EINVAL;
-      return NULL;
-    }
-
   link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
 
   if (link_size > 0)
@@ -5044,14 +5083,6 @@
   return NULL;
 }
 
-ssize_t
-careadlinkatcwd (int fd, char const *filename, char *buffer,
-                 size_t buffer_size)
-{
-  (void) fd;
-  return readlink (filename, buffer, buffer_size);
-}
-
 \f
 /* Support for browsing other processes and their attributes.  See
    process.c for the Lisp bindings.  */


^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-28  8:34   ` Paul Eggert
@ 2013-01-28 14:37     ` Eli Zaretskii
  2013-01-30  0:56       ` Paul Eggert
  0 siblings, 1 reply; 9+ messages in thread
From: Eli Zaretskii @ 2013-01-28 14:37 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 13539

> Date: Mon, 28 Jan 2013 00:34:49 -0800
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 13539@debbugs.gnu.org
> 
> > Is it really worth our while to spend energy on speeding up an API
> > that goes mostly unused in Emacs?  Its only heavy user is ls-lisp.el,
> 
> This patch also improves the performance of file-name-completion
> -- perhaps you missed that part?

I just didn't imagine that file-name-completion would need to call
'lstat'.  But now I see that it does, even twice in a row -- only so
it could support FOO/ in completion-ignored-extensions.  So perhaps a
further optimization would be to avoid the call to
file_name_completion_stat if completion-ignored-extensions is devoid
of elements that end in a slash.

> Better than that, w32 can model the POSIX file descriptor by
> using an int that represents the directory name.

Yes, this is indeed much simpler, thanks.  I have only one comment:

> +static DIR *
> +open_directory (char const *name, int *fdp)
> +{
> +  DIR *d;
> +  int fd, opendir_errno;
> +
> +  block_input ();
> +
> +#ifdef DOS_NT
> +  /* Directories cannot be opened, so emulate an open directory with
> +     its name, cast to a pointer.  This is good enough for Emacs.  */
> +  fd = (int) name;
          ^^^^^
I think we should use ptrdiff_t rather than int here, since that could
make a difference in 64-bit builds on Windows.





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-28 14:37     ` Eli Zaretskii
@ 2013-01-30  0:56       ` Paul Eggert
  2013-01-30 17:27         ` Eli Zaretskii
  2013-01-30 19:24         ` Stefan Monnier
  0 siblings, 2 replies; 9+ messages in thread
From: Paul Eggert @ 2013-01-30  0:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 13539

[-- Attachment #1: Type: text/plain, Size: 709 bytes --]

On 01/28/2013 06:37 AM, Eli Zaretskii wrote:
> perhaps a
> further optimization would be to avoid the call to
> file_name_completion_stat if completion-ignored-extensions is devoid
> of elements that end in a slash.

Yes, something like that might make sense.

>> +  fd = (int) name;
>           ^^^^^
> I think we should use ptrdiff_t rather than int here, since that could
> make a difference in 64-bit builds on Windows.

Unfortunately that won't work because the existing API uses int elsewhere
and we can't easily change int to ptrdiff_t there.
But there's another approach: simply set fd = 0, and use 
dir_pathname inside w32.c.  This should be good enough for what Emacs needs.
Revised patch attached.

[-- Attachment #2: fdopendis.txt --]
[-- Type: text/plain, Size: 79491 bytes --]

=== modified file '.bzrignore'
--- .bzrignore	2013-01-23 23:49:46 +0000
+++ .bzrignore	2013-01-24 06:51:25 +0000
@@ -87,6 +87,7 @@
 lib/alloca.h
 lib/arg-nonnull.h
 lib/c++defs.h
+lib/dirent.h
 lib/execinfo.h
 lib/fcntl.h
 lib/getopt.h

=== modified file 'ChangeLog'
--- ChangeLog	2013-01-23 23:49:46 +0000
+++ ChangeLog	2013-01-29 20:49:27 +0000
@@ -1,3 +1,23 @@
+2013-01-29  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	On my host, this speeds up directory-files-and-attributes by a
+	factor of 3, when applied to Emacs's src directory.
+	These functions are standardized by POSIX and are common these
+	days; fall back on a (slower) gnulib implementation if the host
+	is too old to supply them.
+	* .bzrignore: Add lib/dirent.h.
+	* lib/Makefile.am (libgnu_a_SOURCES): Add openat-die.c, save-cwd.c.
+	* lib/careadlinkat.c, lib/careadlinkat.h: Merge from gnulib,
+	incorporating: 2013-01-29 careadlinkat: do not provide careadlinkatcwd.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+	* lib/dirent.in.h, lib/fdopendir.c, lib/fstatat.c, lib/openat-priv.h:
+	* lib/openat-proc.c, lib/openat.h, m4/dirent_h.m4, m4/fdopendir.m4:
+	* m4/fstatat.m4: New files, from gnulib.
+	* lib/openat-die.c, lib/save-cwd.c, lib/save-cwd.h: New files.
+	These last three are specific to Emacs and are not copied from gnulib.
+	They are simpler than the gnulib versions and are tuned for Emacs.
+
 2013-01-23  Giorgos Keramidas  <gkeramidas@gmail.com>  (tiny change)
 
 	* .bzrignore: add lib-src/blessmail.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2013-01-15 09:22:25 +0000
+++ admin/ChangeLog	2013-01-29 21:03:25 +0000
@@ -1,3 +1,10 @@
+2013-01-29  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	* merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat.
+	(GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h.
+	Avoid dup, open, opendir.
+
 2013-01-15  Dmitry Antipov  <dmantipov@yandex.ru>
 
 	* coccinelle/xsave.cocci: Semantic patch to adjust users of

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2013-01-02 16:13:04 +0000
+++ admin/merge-gnulib	2013-01-24 08:02:16 +0000
@@ -29,9 +29,9 @@
   alloca-opt c-ctype c-strcase
   careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
   dtoastr dtotimespec dup2 environ execinfo faccessat
-  fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday
+  fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
-  manywarnings mktime pselect pthread_sigmask putenv readlink
+  manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat
   sig2str socklen stat-time stdalign stdarg stdbool stdio
   strftime strtoimax strtoumax symlink sys_stat
   sys_time time timer-time timespec-add timespec-sub unsetenv utimens
@@ -39,10 +39,10 @@
 '
 
 GNULIB_TOOL_FLAGS='
-  --avoid=at-internal
+  --avoid=dup
   --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat
   --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
-  --avoid=openat-die --avoid=openat-h
+  --avoid=open --avoid=openat-die --avoid=opendir
   --avoid=raise
   --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib

=== modified file 'lib/Makefile.am'
--- lib/Makefile.am	2012-08-02 10:39:47 +0000
+++ lib/Makefile.am	2013-01-24 06:51:25 +0000
@@ -8,3 +8,5 @@
 DEFAULT_INCLUDES = -I. -I$(top_srcdir)/lib -I../src -I$(top_srcdir)/src
 
 include gnulib.mk
+
+libgnu_a_SOURCES += openat-die.c save-cwd.c

=== modified file 'lib/careadlinkat.c'
--- lib/careadlinkat.c	2013-01-01 09:11:05 +0000
+++ lib/careadlinkat.c	2013-01-29 20:49:27 +0000
@@ -24,9 +24,7 @@
 
 #include <errno.h>
 #include <limits.h>
-#include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
 /* Define this independently so that stdint.h is not a prerequisite.  */
 #ifndef SIZE_MAX
@@ -39,20 +37,6 @@
 
 #include "allocator.h"
 
-/* Get the symbolic link value of FILENAME and put it into BUFFER, with
-   size BUFFER_SIZE.  This function acts like readlink  but has
-   readlinkat's signature.  */
-ssize_t
-careadlinkatcwd (int fd, char const *filename, char *buffer,
-                 size_t buffer_size)
-{
-  /* FD must be AT_FDCWD here, otherwise the caller is using this
-     function in contexts for which it was not meant for.  */
-  if (fd != AT_FDCWD)
-    abort ();
-  return readlink (filename, buffer, buffer_size);
-}
-
 /* Assuming the current directory is FD, get the symbolic link value
    of FILENAME as a null-terminated string and put it into a buffer.
    If FD is AT_FDCWD, FILENAME is interpreted relative to the current

=== modified file 'lib/careadlinkat.h'
--- lib/careadlinkat.h	2013-01-01 09:11:05 +0000
+++ lib/careadlinkat.h	2013-01-29 20:49:27 +0000
@@ -52,9 +52,9 @@
                     ssize_t (*preadlinkat) (int, char const *,
                                             char *, size_t));
 
-/* Suitable values for careadlinkat's FD and PREADLINKAT arguments,
+/* Suitable value for careadlinkat's FD argument,
    when doing a plain readlink:
-   Pass FD = AT_FDCWD and PREADLINKAT = careadlinkatcwd.  */
+   Pass FD = AT_FDCWD.  */
 #if HAVE_READLINKAT
 /* AT_FDCWD is declared in <fcntl.h>.  */
 #else
@@ -66,7 +66,5 @@
 #  define AT_FDCWD (-3041965)
 # endif
 #endif
-ssize_t careadlinkatcwd (int fd, char const *filename,
-                         char *buffer, size_t buffer_size);
 
 #endif /* _GL_CAREADLINKAT_H */

=== added file 'lib/dirent.in.h'
--- lib/dirent.in.h	1970-01-01 00:00:00 +0000
+++ lib/dirent.in.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,258 @@
+/* A GNU-like <dirent.h>.
+   Copyright (C) 2006-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _@GUARD_PREFIX@_DIRENT_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_DIRENT_H@
+# @INCLUDE_NEXT@ @NEXT_DIRENT_H@
+#endif
+
+#ifndef _@GUARD_PREFIX@_DIRENT_H
+#define _@GUARD_PREFIX@_DIRENT_H
+
+/* Get ino_t.  Needed on some systems, including glibc 2.8.  */
+#include <sys/types.h>
+
+#if !@HAVE_DIRENT_H@
+/* Define types DIR and 'struct dirent'.  */
+# if !GNULIB_defined_struct_dirent
+struct dirent
+{
+  char d_type;
+  char d_name[1];
+};
+/* Possible values for 'd_type'.  */
+#  define DT_UNKNOWN 0
+#  define DT_FIFO    1          /* FIFO */
+#  define DT_CHR     2          /* character device */
+#  define DT_DIR     4          /* directory */
+#  define DT_BLK     6          /* block device */
+#  define DT_REG     8          /* regular file */
+#  define DT_LNK    10          /* symbolic link */
+#  define DT_SOCK   12          /* socket */
+#  define DT_WHT    14          /* whiteout */
+typedef struct gl_directory DIR;
+#  define GNULIB_defined_struct_dirent 1
+# endif
+#endif
+
+/* The __attribute__ feature is available in gcc versions 2.5 and later.
+   The attribute __pure__ was added in gcc 2.96.  */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
+#else
+# define _GL_ATTRIBUTE_PURE /* empty */
+#endif
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here.  */
+
+/* The definition of _GL_ARG_NONNULL is copied here.  */
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* Declare overridden functions.  */
+
+#if @GNULIB_OPENDIR@
+# if @REPLACE_OPENDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef opendir
+#   define opendir rpl_opendir
+#  endif
+_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name));
+# else
+#  if !@HAVE_OPENDIR@
+_GL_FUNCDECL_SYS (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (opendir, DIR *, (const char *dir_name));
+# endif
+_GL_CXXALIASWARN (opendir);
+#elif defined GNULIB_POSIXCHECK
+# undef opendir
+# if HAVE_RAW_DECL_OPENDIR
+_GL_WARN_ON_USE (opendir, "opendir is not portable - "
+                 "use gnulib module opendir for portability");
+# endif
+#endif
+
+#if @GNULIB_READDIR@
+# if !@HAVE_READDIR@
+_GL_FUNCDECL_SYS (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (readdir, struct dirent *, (DIR *dirp));
+_GL_CXXALIASWARN (readdir);
+#elif defined GNULIB_POSIXCHECK
+# undef readdir
+# if HAVE_RAW_DECL_READDIR
+_GL_WARN_ON_USE (readdir, "readdir is not portable - "
+                 "use gnulib module readdir for portability");
+# endif
+#endif
+
+#if @GNULIB_REWINDDIR@
+# if !@HAVE_REWINDDIR@
+_GL_FUNCDECL_SYS (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (rewinddir, void, (DIR *dirp));
+_GL_CXXALIASWARN (rewinddir);
+#elif defined GNULIB_POSIXCHECK
+# undef rewinddir
+# if HAVE_RAW_DECL_REWINDDIR
+_GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - "
+                 "use gnulib module rewinddir for portability");
+# endif
+#endif
+
+#if @GNULIB_CLOSEDIR@
+# if @REPLACE_CLOSEDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef closedir
+#   define closedir rpl_closedir
+#  endif
+_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp));
+# else
+#  if !@HAVE_CLOSEDIR@
+_GL_FUNCDECL_SYS (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (closedir, int, (DIR *dirp));
+# endif
+_GL_CXXALIASWARN (closedir);
+#elif defined GNULIB_POSIXCHECK
+# undef closedir
+# if HAVE_RAW_DECL_CLOSEDIR
+_GL_WARN_ON_USE (closedir, "closedir is not portable - "
+                 "use gnulib module closedir for portability");
+# endif
+#endif
+
+#if @GNULIB_DIRFD@
+/* Return the file descriptor associated with the given directory stream,
+   or -1 if none exists.  */
+# if @REPLACE_DIRFD@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef dirfd
+#   define dirfd rpl_dirfd
+#  endif
+_GL_FUNCDECL_RPL (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (dirfd, int, (DIR *));
+# else
+#  if defined __cplusplus && defined GNULIB_NAMESPACE && defined dirfd
+    /* dirfd is defined as a macro and not as a function.
+       Turn it into a function and get rid of the macro.  */
+static inline int (dirfd) (DIR *dp) { return dirfd (dp); }
+#   undef dirfd
+#  endif
+#  if !(@HAVE_DECL_DIRFD@ || defined dirfd)
+_GL_FUNCDECL_SYS (dirfd, int, (DIR *) _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (dirfd, int, (DIR *));
+# endif
+_GL_CXXALIASWARN (dirfd);
+#elif defined GNULIB_POSIXCHECK
+# undef dirfd
+# if HAVE_RAW_DECL_DIRFD
+_GL_WARN_ON_USE (dirfd, "dirfd is unportable - "
+                 "use gnulib module dirfd for portability");
+# endif
+#endif
+
+#if @GNULIB_FDOPENDIR@
+/* Open a directory stream visiting the given directory file
+   descriptor.  Return NULL and set errno if fd is not visiting a
+   directory.  On success, this function consumes fd (it will be
+   implicitly closed either by this function or by a subsequent
+   closedir).  */
+# if @REPLACE_FDOPENDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fdopendir
+#   define fdopendir rpl_fdopendir
+#  endif
+_GL_FUNCDECL_RPL (fdopendir, DIR *, (int fd));
+_GL_CXXALIAS_RPL (fdopendir, DIR *, (int fd));
+# else
+#  if !@HAVE_FDOPENDIR@ || !@HAVE_DECL_FDOPENDIR@
+_GL_FUNCDECL_SYS (fdopendir, DIR *, (int fd));
+#  endif
+_GL_CXXALIAS_SYS (fdopendir, DIR *, (int fd));
+# endif
+_GL_CXXALIASWARN (fdopendir);
+#elif defined GNULIB_POSIXCHECK
+# undef fdopendir
+# if HAVE_RAW_DECL_FDOPENDIR
+_GL_WARN_ON_USE (fdopendir, "fdopendir is unportable - "
+                 "use gnulib module fdopendir for portability");
+# endif
+#endif
+
+#if @GNULIB_SCANDIR@
+/* Scan the directory DIR, calling FILTER on each directory entry.
+   Entries for which FILTER returns nonzero are individually malloc'd,
+   sorted using qsort with CMP, and collected in a malloc'd array in
+   *NAMELIST.  Returns the number of entries selected, or -1 on error.  */
+# if !@HAVE_SCANDIR@
+_GL_FUNCDECL_SYS (scandir, int,
+                  (const char *dir, struct dirent ***namelist,
+                   int (*filter) (const struct dirent *),
+                   int (*cmp) (const struct dirent **, const struct dirent **))
+                  _GL_ARG_NONNULL ((1, 2, 4)));
+# endif
+/* Need to cast, because on glibc systems, the fourth parameter is
+                        int (*cmp) (const void *, const void *).  */
+_GL_CXXALIAS_SYS_CAST (scandir, int,
+                       (const char *dir, struct dirent ***namelist,
+                        int (*filter) (const struct dirent *),
+                        int (*cmp) (const struct dirent **, const struct dirent **)));
+_GL_CXXALIASWARN (scandir);
+#elif defined GNULIB_POSIXCHECK
+# undef scandir
+# if HAVE_RAW_DECL_SCANDIR
+_GL_WARN_ON_USE (scandir, "scandir is unportable - "
+                 "use gnulib module scandir for portability");
+# endif
+#endif
+
+#if @GNULIB_ALPHASORT@
+/* Compare two 'struct dirent' entries alphabetically.  */
+# if !@HAVE_ALPHASORT@
+_GL_FUNCDECL_SYS (alphasort, int,
+                  (const struct dirent **, const struct dirent **)
+                  _GL_ATTRIBUTE_PURE
+                  _GL_ARG_NONNULL ((1, 2)));
+# endif
+/* Need to cast, because on glibc systems, the parameters are
+                       (const void *, const void *).  */
+_GL_CXXALIAS_SYS_CAST (alphasort, int,
+                       (const struct dirent **, const struct dirent **));
+_GL_CXXALIASWARN (alphasort);
+#elif defined GNULIB_POSIXCHECK
+# undef alphasort
+# if HAVE_RAW_DECL_ALPHASORT
+_GL_WARN_ON_USE (alphasort, "alphasort is unportable - "
+                 "use gnulib module alphasort for portability");
+# endif
+#endif
+
+
+#endif /* _@GUARD_PREFIX@_DIRENT_H */
+#endif /* _@GUARD_PREFIX@_DIRENT_H */

=== added file 'lib/fdopendir.c'
--- lib/fdopendir.c	1970-01-01 00:00:00 +0000
+++ lib/fdopendir.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,204 @@
+/* provide a replacement fdopendir function
+   Copyright (C) 2004-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#if !HAVE_FDOPENDIR
+
+# include "openat.h"
+# include "openat-priv.h"
+# include "save-cwd.h"
+
+# if GNULIB_DIRENT_SAFER
+#  include "dirent--.h"
+# endif
+
+# ifndef REPLACE_FCHDIR
+#  define REPLACE_FCHDIR 0
+# endif
+
+static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
+static DIR *fd_clone_opendir (int, struct saved_cwd const *);
+
+/* Replacement for POSIX fdopendir.
+
+   First, try to simulate it via opendir ("/proc/self/fd/...").  Failing
+   that, simulate it by using fchdir metadata, or by doing
+   save_cwd/fchdir/opendir(".")/restore_cwd.
+   If either the save_cwd or the restore_cwd fails (relatively unlikely),
+   then give a diagnostic and exit nonzero.
+
+   If successful, the resulting stream is based on FD in
+   implementations where streams are based on file descriptors and in
+   applications where no other thread or signal handler allocates or
+   frees file descriptors.  In other cases, consult dirfd on the result
+   to find out whether FD is still being used.
+
+   Otherwise, this function works just like POSIX fdopendir.
+
+   W A R N I N G:
+
+   Unlike other fd-related functions, this one places constraints on FD.
+   If this function returns successfully, FD is under control of the
+   dirent.h system, and the caller should not close or modify the state of
+   FD other than by the dirent.h functions.  */
+DIR *
+fdopendir (int fd)
+{
+  DIR *dir = fdopendir_with_dup (fd, -1, NULL);
+
+  if (! REPLACE_FCHDIR && ! dir)
+    {
+      int saved_errno = errno;
+      if (EXPECTED_ERRNO (saved_errno))
+        {
+          struct saved_cwd cwd;
+          if (save_cwd (&cwd) != 0)
+            openat_save_fail (errno);
+          dir = fdopendir_with_dup (fd, -1, &cwd);
+          saved_errno = errno;
+          free_cwd (&cwd);
+          errno = saved_errno;
+        }
+    }
+
+  return dir;
+}
+
+/* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
+   to be a dup of FD which is less than FD - 1 and which will be
+   closed by the caller and not otherwise used by the caller.  This
+   function makes sure that FD is closed and all file descriptors less
+   than FD are open, and then calls fd_clone_opendir on a dup of FD.
+   That way, barring race conditions, fd_clone_opendir returns a
+   stream whose file descriptor is FD.
+
+   If REPLACE_CHDIR or CWD is null, use opendir ("/proc/self/fd/...",
+   falling back on fchdir metadata.  Otherwise, CWD is a saved version
+   of the working directory; use fchdir/opendir(".")/restore_cwd(CWD).  */
+static DIR *
+fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
+{
+  int dupfd = dup (fd);
+  if (dupfd < 0 && errno == EMFILE)
+    dupfd = older_dupfd;
+  if (dupfd < 0)
+    return NULL;
+  else
+    {
+      DIR *dir;
+      int saved_errno;
+      if (dupfd < fd - 1 && dupfd != older_dupfd)
+        {
+          dir = fdopendir_with_dup (fd, dupfd, cwd);
+          saved_errno = errno;
+        }
+      else
+        {
+          close (fd);
+          dir = fd_clone_opendir (dupfd, cwd);
+          saved_errno = errno;
+          if (! dir)
+            {
+              int fd1 = dup (dupfd);
+              if (fd1 != fd)
+                openat_save_fail (fd1 < 0 ? errno : EBADF);
+            }
+        }
+
+      if (dupfd != older_dupfd)
+        close (dupfd);
+      errno = saved_errno;
+      return dir;
+    }
+}
+
+/* Like fdopendir, except the result controls a clone of FD.  It is
+   the caller's responsibility both to close FD and (if the result is
+   not null) to closedir the result.  */
+static DIR *
+fd_clone_opendir (int fd, struct saved_cwd const *cwd)
+{
+  if (REPLACE_FCHDIR || ! cwd)
+    {
+      DIR *dir = NULL;
+      int saved_errno = EOPNOTSUPP;
+      char buf[OPENAT_BUFFER_SIZE];
+      char *proc_file = openat_proc_name (buf, fd, ".");
+      if (proc_file)
+        {
+          dir = opendir (proc_file);
+          saved_errno = errno;
+          if (proc_file != buf)
+            free (proc_file);
+        }
+# if REPLACE_FCHDIR
+      if (! dir && EXPECTED_ERRNO (saved_errno))
+        {
+          char const *name = _gl_directory_name (fd);
+          return (name ? opendir (name) : NULL);
+        }
+# endif
+      errno = saved_errno;
+      return dir;
+    }
+  else
+    {
+      if (fchdir (fd) != 0)
+        return NULL;
+      else
+        {
+          DIR *dir = opendir (".");
+          int saved_errno = errno;
+          if (restore_cwd (cwd) != 0)
+            openat_restore_fail (errno);
+          errno = saved_errno;
+          return dir;
+        }
+    }
+}
+
+#else /* HAVE_FDOPENDIR */
+
+# include <errno.h>
+# include <sys/stat.h>
+
+# undef fdopendir
+
+/* Like fdopendir, but work around GNU/Hurd bug by validating FD.  */
+
+DIR *
+rpl_fdopendir (int fd)
+{
+  struct stat st;
+  if (fstat (fd, &st))
+    return NULL;
+  if (!S_ISDIR (st.st_mode))
+    {
+      errno = ENOTDIR;
+      return NULL;
+    }
+  return fdopendir (fd);
+}
+
+#endif /* HAVE_FDOPENDIR */

=== added file 'lib/fstatat.c'
--- lib/fstatat.c	1970-01-01 00:00:00 +0000
+++ lib/fstatat.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,135 @@
+/* Work around an fstatat bug on Solaris 9.
+
+   Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert and Jim Meyering.  */
+
+/* If the user's config.h happens to include <sys/stat.h>, let it include only
+   the system's <sys/stat.h> here, so that orig_fstatat doesn't recurse to
+   rpl_fstatat.  */
+#define __need_system_sys_stat_h
+#include <config.h>
+
+/* Get the original definition of fstatat.  It might be defined as a macro.  */
+#include <sys/types.h>
+#include <sys/stat.h>
+#undef __need_system_sys_stat_h
+
+#if HAVE_FSTATAT
+static int
+orig_fstatat (int fd, char const *filename, struct stat *buf, int flags)
+{
+  return fstatat (fd, filename, buf, flags);
+}
+#endif
+
+/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
+   eliminates this include because of the preliminary #include <sys/stat.h>
+   above.  */
+#include "sys/stat.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG
+
+# ifndef LSTAT_FOLLOWS_SLASHED_SYMLINK
+#  define LSTAT_FOLLOWS_SLASHED_SYMLINK 0
+# endif
+
+/* fstatat should always follow symbolic links that end in /, but on
+   Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
+   Likewise, trailing slash on a non-directory should be an error.
+   These are the same problems that lstat.c and stat.c address, so
+   solve it in a similar way.
+
+   AIX 7.1 fstatat (AT_FDCWD, ..., 0) always fails, which is a bug.
+   Work around this bug if FSTATAT_AT_FDCWD_0_BROKEN is nonzero.  */
+
+int
+rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
+{
+  int result = orig_fstatat (fd, file, st, flag);
+  size_t len;
+
+  if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0)
+    return result;
+  len = strlen (file);
+  if (flag & AT_SYMLINK_NOFOLLOW)
+    {
+      /* Fix lstat behavior.  */
+      if (file[len - 1] != '/' || S_ISDIR (st->st_mode))
+        return 0;
+      if (!S_ISLNK (st->st_mode))
+        {
+          errno = ENOTDIR;
+          return -1;
+        }
+      result = orig_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
+    }
+  /* Fix stat behavior.  */
+  if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
+    {
+      errno = ENOTDIR;
+      return -1;
+    }
+  return result;
+}
+
+#else /* ! (HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG) */
+
+/* On mingw, the gnulib <sys/stat.h> defines 'stat' as a function-like
+   macro; but using it in AT_FUNC_F2 causes compilation failure
+   because the preprocessor sees a use of a macro that requires two
+   arguments but is only given one.  Hence, we need an inline
+   forwarder to get past the preprocessor.  */
+static int
+stat_func (char const *name, struct stat *st)
+{
+  return stat (name, st);
+}
+
+/* Likewise, if there is no native 'lstat', then the gnulib
+   <sys/stat.h> defined it as stat, which also needs adjustment.  */
+# if !HAVE_LSTAT
+#  undef lstat
+#  define lstat stat_func
+# endif
+
+/* Replacement for Solaris' function by the same name.
+   <http://www.google.com/search?q=fstatat+site:docs.sun.com>
+   First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
+   Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
+   If either the save_cwd or the restore_cwd fails (relatively unlikely),
+   then give a diagnostic and exit nonzero.
+   Otherwise, this function works just like Solaris' fstatat.  */
+
+# define AT_FUNC_NAME fstatat
+# define AT_FUNC_F1 lstat
+# define AT_FUNC_F2 stat_func
+# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
+# define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
+# define AT_FUNC_POST_FILE_ARGS        , st
+# include "at-func.c"
+# undef AT_FUNC_NAME
+# undef AT_FUNC_F1
+# undef AT_FUNC_F2
+# undef AT_FUNC_USE_F1_COND
+# undef AT_FUNC_POST_FILE_PARAM_DECLS
+# undef AT_FUNC_POST_FILE_ARGS
+
+#endif /* !HAVE_FSTATAT */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2013-01-04 02:17:49 +0000
+++ lib/gnulib.mk	2013-01-24 08:02:16 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=at-internal --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=openat-die --avoid=openat-h --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --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=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -64,6 +64,17 @@
 
 ## end   gnulib module allocator
 
+## begin gnulib module at-internal
+
+if gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b
+
+endif
+EXTRA_DIST += openat-priv.h openat-proc.c
+
+EXTRA_libgnu_a_SOURCES += openat-proc.c
+
+## end   gnulib module at-internal
+
 ## begin gnulib module c-ctype
 
 libgnu_a_SOURCES += c-ctype.h c-ctype.c
@@ -124,6 +135,54 @@
 
 ## end   gnulib module crypto/sha512
 
+## begin gnulib module dirent
+
+BUILT_SOURCES += dirent.h
+
+# We need the following in order to create <dirent.h> when the system
+# doesn't have one that works with the given compiler.
+dirent.h: dirent.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  sed -e 's|@''GUARD_PREFIX''@|GL|g' \
+	      -e 's|@''HAVE_DIRENT_H''@|$(HAVE_DIRENT_H)|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \
+	      -e 's/@''GNULIB_OPENDIR''@/$(GNULIB_OPENDIR)/g' \
+	      -e 's/@''GNULIB_READDIR''@/$(GNULIB_READDIR)/g' \
+	      -e 's/@''GNULIB_REWINDDIR''@/$(GNULIB_REWINDDIR)/g' \
+	      -e 's/@''GNULIB_CLOSEDIR''@/$(GNULIB_CLOSEDIR)/g' \
+	      -e 's/@''GNULIB_DIRFD''@/$(GNULIB_DIRFD)/g' \
+	      -e 's/@''GNULIB_FDOPENDIR''@/$(GNULIB_FDOPENDIR)/g' \
+	      -e 's/@''GNULIB_SCANDIR''@/$(GNULIB_SCANDIR)/g' \
+	      -e 's/@''GNULIB_ALPHASORT''@/$(GNULIB_ALPHASORT)/g' \
+	      -e 's/@''HAVE_OPENDIR''@/$(HAVE_OPENDIR)/g' \
+	      -e 's/@''HAVE_READDIR''@/$(HAVE_READDIR)/g' \
+	      -e 's/@''HAVE_REWINDDIR''@/$(HAVE_REWINDDIR)/g' \
+	      -e 's/@''HAVE_CLOSEDIR''@/$(HAVE_CLOSEDIR)/g' \
+	      -e 's|@''HAVE_DECL_DIRFD''@|$(HAVE_DECL_DIRFD)|g' \
+	      -e 's|@''HAVE_DECL_FDOPENDIR''@|$(HAVE_DECL_FDOPENDIR)|g' \
+	      -e 's|@''HAVE_FDOPENDIR''@|$(HAVE_FDOPENDIR)|g' \
+	      -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \
+	      -e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \
+	      -e 's|@''REPLACE_OPENDIR''@|$(REPLACE_OPENDIR)|g' \
+	      -e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \
+	      -e 's|@''REPLACE_DIRFD''@|$(REPLACE_DIRFD)|g' \
+	      -e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \
+	      -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+	      -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+	      -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+	      < $(srcdir)/dirent.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+MOSTLYCLEANFILES += dirent.h dirent.h-t
+
+EXTRA_DIST += dirent.in.h
+
+## end   gnulib module dirent
+
 ## begin gnulib module dosname
 
 if gl_GNULIB_ENABLED_dosname
@@ -238,6 +297,15 @@
 
 ## end   gnulib module fcntl-h
 
+## begin gnulib module fdopendir
+
+
+EXTRA_DIST += fdopendir.c
+
+EXTRA_libgnu_a_SOURCES += fdopendir.c
+
+## end   gnulib module fdopendir
+
 ## begin gnulib module filemode
 
 libgnu_a_SOURCES += filemode.c
@@ -255,6 +323,15 @@
 
 ## end   gnulib module fpending
 
+## begin gnulib module fstatat
+
+
+EXTRA_DIST += at-func.c fstatat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c fstatat.c
+
+## end   gnulib module fstatat
+
 ## begin gnulib module getgroups
 
 if gl_GNULIB_ENABLED_getgroups
@@ -412,6 +489,15 @@
 
 ## end   gnulib module mktime
 
+## begin gnulib module openat-h
+
+if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
+
+endif
+EXTRA_DIST += openat.h
+
+## end   gnulib module openat-h
+
 ## begin gnulib module pathmax
 
 if gl_GNULIB_ENABLED_pathmax
@@ -457,6 +543,15 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module readlinkat
+
+
+EXTRA_DIST += at-func.c readlinkat.c
+
+EXTRA_libgnu_a_SOURCES += at-func.c readlinkat.c
+
+## end   gnulib module readlinkat
+
 ## begin gnulib module root-uid
 
 if gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c

=== added file 'lib/openat-die.c'
--- lib/openat-die.c	1970-01-01 00:00:00 +0000
+++ lib/openat-die.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,6 @@
+/* Respond to a save- or restore-cwd failure.
+   This should never happen with Emacs.  */
+#include <config.h>
+#include "openat.h"
+void openat_save_fail (int errnum) { abort (); }
+void openat_restore_fail (int errnum) { abort (); }

=== added file 'lib/openat-priv.h'
--- lib/openat-priv.h	1970-01-01 00:00:00 +0000
+++ lib/openat-priv.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,64 @@
+/* Internals for openat-like functions.
+
+   Copyright (C) 2005-2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#ifndef _GL_HEADER_OPENAT_PRIV
+#define _GL_HEADER_OPENAT_PRIV
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/* Maximum number of bytes that it is safe to allocate as a single
+   array on the stack, and that is known as a compile-time constant.
+   The assumption is that we'll touch the array very quickly, or a
+   temporary very near the array, provoking an out-of-memory trap.  On
+   some operating systems, there is only one guard page for the stack,
+   and a page size can be as small as 4096 bytes.  Subtract 64 in the
+   hope that this will let the compiler touch a nearby temporary and
+   provoke a trap.  */
+#define SAFER_ALLOCA_MAX (4096 - 64)
+
+#define SAFER_ALLOCA(m) ((m) < SAFER_ALLOCA_MAX ? (m) : SAFER_ALLOCA_MAX)
+
+#if defined PATH_MAX
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (PATH_MAX)
+#elif defined _XOPEN_PATH_MAX
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (_XOPEN_PATH_MAX)
+#else
+# define OPENAT_BUFFER_SIZE SAFER_ALLOCA (1024)
+#endif
+
+char *openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file);
+
+/* Trying to access a BUILD_PROC_NAME file will fail on systems without
+   /proc support, and even on systems *with* ProcFS support.  Return
+   nonzero if the failure may be legitimate, e.g., because /proc is not
+   readable, or the particular .../fd/N directory is not present.  */
+#define EXPECTED_ERRNO(Errno)                   \
+  ((Errno) == ENOTDIR || (Errno) == ENOENT      \
+   || (Errno) == EPERM || (Errno) == EACCES     \
+   || (Errno) == ENOSYS /* Solaris 8 */         \
+   || (Errno) == EOPNOTSUPP /* FreeBSD */)
+
+/* Wrapper function shared among linkat and renameat.  */
+int at_func2 (int fd1, char const *file1,
+              int fd2, char const *file2,
+              int (*func) (char const *file1, char const *file2));
+
+#endif /* _GL_HEADER_OPENAT_PRIV */

=== added file 'lib/openat-proc.c'
--- lib/openat-proc.c	1970-01-01 00:00:00 +0000
+++ lib/openat-proc.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,110 @@
+/* Create /proc/self/fd-related names for subfiles of open directories.
+
+   Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+#include <config.h>
+
+#include "openat-priv.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "intprops.h"
+
+/* The results of open() in this file are not used with fchdir,
+   and we do not leak fds to any single-threaded code that could use stdio,
+   therefore save some unnecessary work in fchdir.c.
+   FIXME - if the kernel ever adds support for multi-thread safety for
+   avoiding standard fds, then we should use open_safer.  */
+#undef open
+#undef close
+
+#define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/%s"
+
+#define PROC_SELF_FD_NAME_SIZE_BOUND(len) \
+  (sizeof PROC_SELF_FD_FORMAT - sizeof "%d%s" \
+   + INT_STRLEN_BOUND (int) + (len) + 1)
+
+
+/* Set BUF to the expansion of PROC_SELF_FD_FORMAT, using FD and FILE
+   respectively for %d and %s.  If successful, return BUF if the
+   result fits in BUF, dynamically allocated memory otherwise.  But
+   return NULL if /proc is not reliable, either because the operating
+   system support is lacking or because memory is low.  */
+char *
+openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
+{
+  static int proc_status = 0;
+
+  /* Make sure the caller gets ENOENT when appropriate.  */
+  if (!*file)
+    {
+      buf[0] = '\0';
+      return buf;
+    }
+
+  if (! proc_status)
+    {
+      /* Set PROC_STATUS to a positive value if /proc/self/fd is
+         reliable, and a negative value otherwise.  Solaris 10
+         /proc/self/fd mishandles "..", and any file name might expand
+         to ".." after symbolic link expansion, so avoid /proc/self/fd
+         if it mishandles "..".  Solaris 10 has openat, but this
+         problem is exhibited on code that built on Solaris 8 and
+         running on Solaris 10.  */
+
+      int proc_self_fd = open ("/proc/self/fd",
+                               O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
+      if (proc_self_fd < 0)
+        proc_status = -1;
+      else
+        {
+          /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
+             number of a file descriptor open on /proc/self/fd.  On Linux,
+             that name resolves to /proc/self/fd, which was opened above.
+             However, on Solaris, it may resolve to /proc/self/fd/fd, which
+             cannot exist, since all names in /proc/self/fd are numeric.  */
+          char dotdot_buf[PROC_SELF_FD_NAME_SIZE_BOUND (sizeof "../fd" - 1)];
+          sprintf (dotdot_buf, PROC_SELF_FD_FORMAT, proc_self_fd, "../fd");
+          proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
+          close (proc_self_fd);
+        }
+    }
+
+  if (proc_status < 0)
+    return NULL;
+  else
+    {
+      size_t bufsize = PROC_SELF_FD_NAME_SIZE_BOUND (strlen (file));
+      char *result = buf;
+      if (OPENAT_BUFFER_SIZE < bufsize)
+        {
+          result = malloc (bufsize);
+          if (! result)
+            return NULL;
+        }
+      sprintf (result, PROC_SELF_FD_FORMAT, fd, file);
+      return result;
+    }
+}

=== added file 'lib/openat.h'
--- lib/openat.h	1970-01-01 00:00:00 +0000
+++ lib/openat.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,120 @@
+/* provide a replacement openat function
+   Copyright (C) 2004-2006, 2008-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#ifndef _GL_HEADER_OPENAT
+#define _GL_HEADER_OPENAT
+
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+_GL_INLINE_HEADER_BEGIN
+
+#if !HAVE_OPENAT
+
+int openat_permissive (int fd, char const *file, int flags, mode_t mode,
+                       int *cwd_errno);
+bool openat_needs_fchdir (void);
+
+#else
+
+# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \
+    openat (Fd, File, Flags, Mode)
+# define openat_needs_fchdir() false
+
+#endif
+
+_Noreturn void openat_restore_fail (int);
+_Noreturn void openat_save_fail (int);
+
+/* Using these function names makes application code
+   slightly more readable than it would be with
+   fchownat (..., 0) or fchownat (..., AT_SYMLINK_NOFOLLOW).  */
+
+#if GNULIB_FCHOWNAT
+
+# ifndef FCHOWNAT_INLINE
+#  define FCHOWNAT_INLINE _GL_INLINE
+# endif
+
+FCHOWNAT_INLINE int
+chownat (int fd, char const *file, uid_t owner, gid_t group)
+{
+  return fchownat (fd, file, owner, group, 0);
+}
+
+FCHOWNAT_INLINE int
+lchownat (int fd, char const *file, uid_t owner, gid_t group)
+{
+  return fchownat (fd, file, owner, group, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+#if GNULIB_FCHMODAT
+
+# ifndef FCHMODAT_INLINE
+#  define FCHMODAT_INLINE _GL_INLINE
+# endif
+
+FCHMODAT_INLINE int
+chmodat (int fd, char const *file, mode_t mode)
+{
+  return fchmodat (fd, file, mode, 0);
+}
+
+FCHMODAT_INLINE int
+lchmodat (int fd, char const *file, mode_t mode)
+{
+  return fchmodat (fd, file, mode, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+#if GNULIB_STATAT
+
+# ifndef STATAT_INLINE
+#  define STATAT_INLINE _GL_INLINE
+# endif
+
+STATAT_INLINE int
+statat (int fd, char const *name, struct stat *st)
+{
+  return fstatat (fd, name, st, 0);
+}
+
+STATAT_INLINE int
+lstatat (int fd, char const *name, struct stat *st)
+{
+  return fstatat (fd, name, st, AT_SYMLINK_NOFOLLOW);
+}
+
+#endif
+
+/* For now, there are no wrappers named laccessat or leuidaccessat,
+   since gnulib doesn't support faccessat(,AT_SYMLINK_NOFOLLOW) and
+   since access rights on symlinks are of limited utility.  Likewise,
+   wrappers are not provided for accessat or euidaccessat, so as to
+   avoid dragging in -lgen on some platforms.  */
+
+_GL_INLINE_HEADER_END
+
+#endif /* _GL_HEADER_OPENAT */

=== added file 'lib/readlinkat.c'
--- lib/readlinkat.c	1970-01-01 00:00:00 +0000
+++ lib/readlinkat.c	2013-01-24 08:02:16 +0000
@@ -0,0 +1,47 @@
+/* Read a symlink relative to an open directory.
+   Copyright (C) 2009-2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Eric Blake */
+
+#include <config.h>
+
+#include <unistd.h>
+
+/* Gnulib provides a readlink stub for mingw; use it for distinction
+   between EINVAL and ENOENT, rather than always failing with ENOSYS.  */
+
+/* POSIX 2008 says that unlike readlink, readlinkat returns 0 for
+   success instead of the buffer length.  But this would render
+   readlinkat worthless since readlink does not guarantee a
+   NUL-terminated buffer.  Assume this was a bug in POSIX.  */
+
+/* Read the contents of symlink FILE into buffer BUF of size LEN, in the
+   directory open on descriptor FD.  If possible, do it without changing
+   the working directory.  Otherwise, resort to using save_cwd/fchdir,
+   then readlink/restore_cwd.  If either the save_cwd or the restore_cwd
+   fails, then give a diagnostic and exit nonzero.  */
+
+#define AT_FUNC_NAME readlinkat
+#define AT_FUNC_F1 readlink
+#define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len
+#define AT_FUNC_POST_FILE_ARGS        , buf, len
+#define AT_FUNC_RESULT ssize_t
+#include "at-func.c"
+#undef AT_FUNC_NAME
+#undef AT_FUNC_F1
+#undef AT_FUNC_POST_FILE_PARAM_DECLS
+#undef AT_FUNC_POST_FILE_ARGS
+#undef AT_FUNC_RESULT

=== added file 'lib/save-cwd.c'
--- lib/save-cwd.c	1970-01-01 00:00:00 +0000
+++ lib/save-cwd.c	2013-01-24 06:51:25 +0000
@@ -0,0 +1,3 @@
+#include <config.h>
+#define SAVE_CWD_INLINE _GL_EXTERN_INLINE
+#include "save-cwd.h"

=== added file 'lib/save-cwd.h'
--- lib/save-cwd.h	1970-01-01 00:00:00 +0000
+++ lib/save-cwd.h	2013-01-24 06:51:25 +0000
@@ -0,0 +1,46 @@
+/* Do not save and restore the current working directory.
+
+   Copyright 2013 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Gnulib needs to save and restore the current working directory to
+   fully emulate functions like fstatat.  But Emacs doesn't care what
+   the current working directory is; it always uses absolute file
+   names.  This module replaces the Gnulib module by omitting the code
+   that Emacs does not need.  */
+
+#ifndef SAVE_CWD_H
+#define SAVE_CWD_H 1
+
+_GL_INLINE_HEADER_BEGIN
+#ifndef SAVE_CWD_INLINE
+# define SAVE_CWD_INLINE _GL_INLINE
+#endif
+
+struct saved_cwd { int desc; };
+
+SAVE_CWD_INLINE int
+save_cwd (struct saved_cwd *cwd)
+{
+  cwd->desc = -1;
+  return 0;
+}
+
+SAVE_CWD_INLINE int restore_cwd (struct saved_cwd const *cwd) { return 0; }
+SAVE_CWD_INLINE void free_cwd (struct saved_cwd *cwd) { }
+
+_GL_INLINE_HEADER_END
+
+#endif

=== added file 'm4/dirent_h.m4'
--- m4/dirent_h.m4	1970-01-01 00:00:00 +0000
+++ m4/dirent_h.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,64 @@
+# dirent_h.m4 serial 16
+dnl Copyright (C) 2008-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Bruno Haible.
+
+AC_DEFUN([gl_DIRENT_H],
+[
+  dnl Use AC_REQUIRE here, so that the default behavior below is expanded
+  dnl once only, before all statements that occur in other macros.
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+
+  dnl <dirent.h> is always overridden, because of GNULIB_POSIXCHECK.
+  gl_CHECK_NEXT_HEADERS([dirent.h])
+  if test $ac_cv_header_dirent_h = yes; then
+    HAVE_DIRENT_H=1
+  else
+    HAVE_DIRENT_H=0
+  fi
+  AC_SUBST([HAVE_DIRENT_H])
+
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use.
+  gl_WARN_ON_USE_PREPARE([[#include <dirent.h>
+    ]], [alphasort closedir dirfd fdopendir opendir readdir rewinddir scandir])
+])
+
+AC_DEFUN([gl_DIRENT_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+AC_DEFUN([gl_DIRENT_H_DEFAULTS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) dnl for REPLACE_FCHDIR
+  GNULIB_OPENDIR=0;     AC_SUBST([GNULIB_OPENDIR])
+  GNULIB_READDIR=0;     AC_SUBST([GNULIB_READDIR])
+  GNULIB_REWINDDIR=0;   AC_SUBST([GNULIB_REWINDDIR])
+  GNULIB_CLOSEDIR=0;    AC_SUBST([GNULIB_CLOSEDIR])
+  GNULIB_DIRFD=0;       AC_SUBST([GNULIB_DIRFD])
+  GNULIB_FDOPENDIR=0;   AC_SUBST([GNULIB_FDOPENDIR])
+  GNULIB_SCANDIR=0;     AC_SUBST([GNULIB_SCANDIR])
+  GNULIB_ALPHASORT=0;   AC_SUBST([GNULIB_ALPHASORT])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_OPENDIR=1;       AC_SUBST([HAVE_OPENDIR])
+  HAVE_READDIR=1;       AC_SUBST([HAVE_READDIR])
+  HAVE_REWINDDIR=1;     AC_SUBST([HAVE_REWINDDIR])
+  HAVE_CLOSEDIR=1;      AC_SUBST([HAVE_CLOSEDIR])
+  HAVE_DECL_DIRFD=1;    AC_SUBST([HAVE_DECL_DIRFD])
+  HAVE_DECL_FDOPENDIR=1;AC_SUBST([HAVE_DECL_FDOPENDIR])
+  HAVE_FDOPENDIR=1;     AC_SUBST([HAVE_FDOPENDIR])
+  HAVE_SCANDIR=1;       AC_SUBST([HAVE_SCANDIR])
+  HAVE_ALPHASORT=1;     AC_SUBST([HAVE_ALPHASORT])
+  REPLACE_OPENDIR=0;    AC_SUBST([REPLACE_OPENDIR])
+  REPLACE_CLOSEDIR=0;   AC_SUBST([REPLACE_CLOSEDIR])
+  REPLACE_DIRFD=0;      AC_SUBST([REPLACE_DIRFD])
+  REPLACE_FDOPENDIR=0;  AC_SUBST([REPLACE_FDOPENDIR])
+])

=== added file 'm4/fdopendir.m4'
--- m4/fdopendir.m4	1970-01-01 00:00:00 +0000
+++ m4/fdopendir.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,61 @@
+# serial 10
+# See if we need to provide fdopendir.
+
+dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_FDOPENDIR],
+[
+  AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  dnl FreeBSD 7.3 has the function, but failed to declare it.
+  AC_CHECK_DECLS([fdopendir], [], [HAVE_DECL_FDOPENDIR=0], [[
+#include <dirent.h>
+    ]])
+  AC_CHECK_FUNCS_ONCE([fdopendir])
+  if test $ac_cv_func_fdopendir = no; then
+    HAVE_FDOPENDIR=0
+  else
+    AC_CACHE_CHECK([whether fdopendir works],
+      [gl_cv_func_fdopendir_works],
+      [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#if !HAVE_DECL_FDOPENDIR
+extern
+# ifdef __cplusplus
+"C"
+# endif
+DIR *fdopendir (int);
+#endif
+]], [int result = 0;
+     int fd = open ("conftest.c", O_RDONLY);
+     if (fd < 0) result |= 1;
+     if (fdopendir (fd)) result |= 2;
+     if (close (fd)) result |= 4;
+     return result;])],
+         [gl_cv_func_fdopendir_works=yes],
+         [gl_cv_func_fdopendir_works=no],
+         [case "$host_os" in
+                    # Guess yes on glibc systems.
+            *-gnu*) gl_cv_func_fdopendir_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      gl_cv_func_fdopendir_works="guessing no" ;;
+          esac
+         ])])
+    case "$gl_cv_func_fdopendir_works" in
+      *yes) ;;
+      *)
+        REPLACE_FDOPENDIR=1
+        ;;
+    esac
+  fi
+])

=== added file 'm4/fstatat.m4'
--- m4/fstatat.m4	1970-01-01 00:00:00 +0000
+++ m4/fstatat.m4	2013-01-24 06:51:25 +0000
@@ -0,0 +1,60 @@
+# fstatat.m4 serial 3
+dnl Copyright (C) 2004-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Jim Meyering.
+
+# If we have the fstatat function, and it has the bug (in AIX 7.1)
+# that it does not fill in st_size correctly, use the replacement function.
+AC_DEFUN([gl_FUNC_FSTATAT],
+[
+  AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CHECK_FUNCS_ONCE([fstatat])
+
+  if test $ac_cv_func_fstatat = no; then
+    HAVE_FSTATAT=0
+  else
+    dnl Test for an AIX 7.1 bug; see
+    dnl <http://lists.gnu.org/archive/html/bug-tar/2011-09/msg00015.html>.
+    AC_CACHE_CHECK([whether fstatat (..., 0) works],
+      [gl_cv_func_fstatat_zero_flag],
+      [AC_RUN_IFELSE(
+         [AC_LANG_SOURCE(
+            [[
+              #include <fcntl.h>
+              #include <sys/stat.h>
+              int
+              main (void)
+              {
+                struct stat a;
+                return fstatat (AT_FDCWD, ".", &a, 0) != 0;
+              }
+            ]])],
+         [gl_cv_func_fstatat_zero_flag=yes],
+         [gl_cv_func_fstatat_zero_flag=no],
+         [case "$host_os" in
+            aix*) gl_cv_func_fstatat_zero_flag="guessing no";;
+            *)    gl_cv_func_fstatat_zero_flag="guessing yes";;
+          esac
+         ])
+      ])
+
+    case $gl_cv_func_fstatat_zero_flag+$gl_cv_func_lstat_dereferences_slashed_symlink in
+    *yes+*yes) ;;
+    *) REPLACE_FSTATAT=1
+       case $gl_cv_func_fstatat_zero_flag in
+       *yes)
+         AC_DEFINE([HAVE_WORKING_FSTATAT_ZERO_FLAG], [1],
+           [Define to 1 if fstatat (..., 0) works.
+            For example, it does not work in AIX 7.1.])
+         ;;
+       esac
+       ;;
+    esac
+  fi
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2013-01-04 02:17:49 +0000
+++ m4/gnulib-comp.m4	2013-01-24 08:02:16 +0000
@@ -40,6 +40,7 @@
   AC_REQUIRE([gl_PROG_AR_RANLIB])
   # Code from module alloca-opt:
   # Code from module allocator:
+  # Code from module at-internal:
   # Code from module c-ctype:
   # Code from module c-strcase:
   # Code from module careadlinkat:
@@ -49,6 +50,7 @@
   # Code from module crypto/sha1:
   # Code from module crypto/sha256:
   # Code from module crypto/sha512:
+  # Code from module dirent:
   # Code from module dosname:
   # Code from module dtoastr:
   # Code from module dtotimespec:
@@ -61,8 +63,10 @@
   # Code from module extern-inline:
   # Code from module faccessat:
   # Code from module fcntl-h:
+  # Code from module fdopendir:
   # Code from module filemode:
   # Code from module fpending:
+  # Code from module fstatat:
   # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
@@ -82,11 +86,13 @@
   # Code from module mktime:
   # Code from module multiarch:
   # Code from module nocrash:
+  # Code from module openat-h:
   # Code from module pathmax:
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module putenv:
   # Code from module readlink:
+  # Code from module readlinkat:
   # Code from module root-uid:
   # Code from module sig2str:
   # Code from module signal-h:
@@ -159,6 +165,7 @@
   gl_SHA1
   gl_SHA256
   gl_SHA512
+  gl_DIRENT_H
   AC_REQUIRE([gl_C99_STRTOLD])
   gl_FUNC_DUP2
   if test $HAVE_DUP2 = 0 || test $REPLACE_DUP2 = 1; then
@@ -178,12 +185,23 @@
   gl_MODULE_INDICATOR([faccessat])
   gl_UNISTD_MODULE_INDICATOR([faccessat])
   gl_FCNTL_H
+  gl_FUNC_FDOPENDIR
+  if test $HAVE_FDOPENDIR = 0 || test $REPLACE_FDOPENDIR = 1; then
+    AC_LIBOBJ([fdopendir])
+  fi
+  gl_DIRENT_MODULE_INDICATOR([fdopendir])
+  gl_MODULE_INDICATOR([fdopendir])
   gl_FILEMODE
   gl_FUNC_FPENDING
   if test $ac_cv_func___fpending = no; then
     AC_LIBOBJ([fpending])
     gl_PREREQ_FPENDING
   fi
+  gl_FUNC_FSTATAT
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    AC_LIBOBJ([fstatat])
+  fi
+  gl_SYS_STAT_MODULE_INDICATOR([fstatat])
   gl_GETLOADAVG
   if test $HAVE_GETLOADAVG = 0; then
     AC_LIBOBJ([getloadavg])
@@ -253,6 +271,11 @@
     gl_PREREQ_READLINK
   fi
   gl_UNISTD_MODULE_INDICATOR([readlink])
+  gl_FUNC_READLINKAT
+  if test $HAVE_READLINKAT = 0; then
+    AC_LIBOBJ([readlinkat])
+  fi
+  gl_UNISTD_MODULE_INDICATOR([readlinkat])
   gl_FUNC_SIG2STR
   if test $ac_cv_func_sig2str = no; then
     AC_LIBOBJ([sig2str])
@@ -311,11 +334,13 @@
   fi
   gl_STDLIB_MODULE_INDICATOR([unsetenv])
   gl_UTIMENS
+  gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false
   gl_gnulib_enabled_dosname=false
   gl_gnulib_enabled_euidaccess=false
   gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
   gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
+  gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
   gl_gnulib_enabled_stat=false
@@ -323,6 +348,13 @@
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
   gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
+  func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b ()
+  {
+    if ! $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then
+      AC_LIBOBJ([openat-proc])
+      gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=true
+    fi
+  }
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
@@ -385,6 +417,12 @@
       fi
     fi
   }
+  func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 ()
+  {
+    if ! $gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7; then
+      gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -456,11 +494,29 @@
     fi
   }
   if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FACCESSAT = 0; then
     func_gl_gnulib_m4code_dosname
   fi
   if test $HAVE_FACCESSAT = 0; then
     func_gl_gnulib_m4code_euidaccess
   fi
+  if test $HAVE_FACCESSAT = 0; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
+  if test $HAVE_FDOPENDIR = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -473,6 +529,15 @@
   if test $HAVE_READLINK = 0 || test $REPLACE_READLINK = 1; then
     func_gl_gnulib_m4code_stat
   fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
+  fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_dosname
+  fi
+  if test $HAVE_READLINKAT = 0; then
+    func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7
+  fi
   if { test $HAVE_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then
     func_gl_gnulib_m4code_strtoll
   fi
@@ -486,11 +551,13 @@
     func_gl_gnulib_m4code_verify
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
@@ -656,6 +723,7 @@
   lib/careadlinkat.h
   lib/close-stream.c
   lib/close-stream.h
+  lib/dirent.in.h
   lib/dosname.h
   lib/dtoastr.c
   lib/dtotimespec.c
@@ -665,10 +733,12 @@
   lib/execinfo.in.h
   lib/faccessat.c
   lib/fcntl.in.h
+  lib/fdopendir.c
   lib/filemode.c
   lib/filemode.h
   lib/fpending.c
   lib/fpending.h
+  lib/fstatat.c
   lib/ftoastr.c
   lib/ftoastr.h
   lib/getgroups.c
@@ -689,11 +759,15 @@
   lib/md5.h
   lib/mktime-internal.h
   lib/mktime.c
+  lib/openat-priv.h
+  lib/openat-proc.c
+  lib/openat.h
   lib/pathmax.h
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/putenv.c
   lib/readlink.c
+  lib/readlinkat.c
   lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
@@ -746,6 +820,7 @@
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/close-stream.m4
+  m4/dirent_h.m4
   m4/dup2.m4
   m4/environ.m4
   m4/euidaccess.m4
@@ -755,8 +830,10 @@
   m4/faccessat.m4
   m4/fcntl-o.m4
   m4/fcntl_h.m4
+  m4/fdopendir.m4
   m4/filemode.m4
   m4/fpending.m4
+  m4/fstatat.m4
   m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
@@ -780,6 +857,7 @@
   m4/pthread_sigmask.m4
   m4/putenv.m4
   m4/readlink.m4
+  m4/readlinkat.m4
   m4/setenv.m4
   m4/sha1.m4
   m4/sha256.m4

=== added file 'm4/readlinkat.m4'
--- m4/readlinkat.m4	1970-01-01 00:00:00 +0000
+++ m4/readlinkat.m4	2013-01-24 08:02:16 +0000
@@ -0,0 +1,19 @@
+# serial 3
+# See if we need to provide readlinkat replacement.
+
+dnl Copyright (C) 2009-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Written by Eric Blake.
+
+AC_DEFUN([gl_FUNC_READLINKAT],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_CHECK_FUNCS_ONCE([readlinkat])
+  if test $ac_cv_func_readlinkat = no; then
+    HAVE_READLINKAT=0
+  fi
+])

=== modified file 'nt/ChangeLog'
--- nt/ChangeLog	2013-01-28 14:41:08 +0000
+++ nt/ChangeLog	2013-01-29 21:03:25 +0000
@@ -1,3 +1,9 @@
+2013-01-29  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	* inc/sys/stat.h (fstatat):
+	* inc/unistd.h (readlinkat): New decls.
+
 2013-01-28  Eli Zaretskii  <eliz@gnu.org>
 
 	* inc/dirent.h (opendir): Update prototype.

=== modified file 'nt/inc/sys/stat.h'
--- nt/inc/sys/stat.h	2013-01-02 16:13:04 +0000
+++ nt/inc/sys/stat.h	2013-01-28 08:15:12 +0000
@@ -110,6 +110,7 @@
 _CRTIMP int __cdecl __MINGW_NOTHROW	chmod (const char*, int);
 _CRTIMP int __cdecl __MINGW_NOTHROW	stat (const char*, struct stat*);
 _CRTIMP int __cdecl __MINGW_NOTHROW	lstat (const char*, struct stat*);
+_CRTIMP int __cdecl __MINGW_NOTHROW	fstatat (int, char const *,
+						 struct stat *, int);
 
 #endif	/* INC_SYS_STAT_H_ */
-

=== modified file 'nt/inc/unistd.h'
--- nt/inc/unistd.h	2013-01-11 09:33:54 +0000
+++ nt/inc/unistd.h	2013-01-28 08:15:12 +0000
@@ -12,6 +12,7 @@
    and whose prototypes are usually found in unistd.h on POSIX
    platforms.  */
 extern ssize_t readlink (const char *, char *, size_t);
+extern ssize_t readlinkat (int, const char *, char *, size_t);
 extern int symlink (char const *, char const *);
 extern int setpgid (pid_t, pid_t);
 extern pid_t getpgrp (void);

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2013-01-28 14:41:08 +0000
+++ src/ChangeLog	2013-01-30 00:48:59 +0000
@@ -1,3 +1,34 @@
+2013-01-29  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use fdopendir, fstatat and readlinkat, for efficiency (Bug#13539).
+	* conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove.
+	* dired.c: Include <fcntl.h>.
+	(open_directory): New function, which uses open and fdopendir
+	rather than opendir.  DOS_NT platforms still use opendir, though.
+	(directory_files_internal, file_name_completion): Use it.
+	(file_attributes): New function, with most of the old Ffile_attributes.
+	(directory_files_internal, Ffile_attributes): Use it.
+	(file_attributes, file_name_completion_stat): First arg is now fd,
+	not dir name.  All uses changed.  Use fstatat rather than lstat +
+	stat.
+	(file_attributes): Use emacs_readlinkat rather than Ffile_symlink_p.
+	* fileio.c: Include <allocator.h>, <careadlinkat.h>.
+	(emacs_readlinkat): New function, with much of the old
+	Ffile_symlink_p, but with an fd argument for speed.
+	It uses readlinkat rather than careadlinkatcwd, so that it
+	need not assume the working directory.
+	(Ffile_symlink_p): Use it.
+	* filelock.c (current_lock_owner): Use emacs_readlinkat
+	rather than emacs_readlink.
+	* lisp.h (emacs_readlinkat): New decl.
+	(READLINK_BUFSIZE, emacs_readlink): Remove.
+	* sysdep.c: Do not include <allocator.h>, <careadlinkat.h>.
+	(emacs_norealloc_allocator, emacs_readlink): Remove.
+	This stuff is moved to fileio.c.
+	* w32.c (fstatat, readlinkat): New functions.
+	(careadlinkat): Don't check that fd == AT_FDCWD.
+	(careadlinkatcwd): Remove; no longer needed.
+
 2013-01-28  Eli Zaretskii  <eliz@gnu.org>
 
 	* w32.c (opendir): Now accepts a 'const char *'.

=== modified file 'src/conf_post.h'
--- src/conf_post.h	2013-01-02 16:13:04 +0000
+++ src/conf_post.h	2013-01-24 06:51:25 +0000
@@ -182,10 +182,6 @@
 #endif
 #endif
 
-/* Tell gnulib to omit support for openat-related functions having a
-   first argument other than AT_FDCWD.  */
-#define GNULIB_SUPPORT_ONLY_AT_FDCWD
-
 #include <string.h>
 #include <stdlib.h>
 

=== modified file 'src/dired.c'
--- src/dired.c	2013-01-17 06:29:40 +0000
+++ src/dired.c	2013-01-29 20:49:27 +0000
@@ -30,6 +30,7 @@
 #include <grp.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 #include <dirent.h>
@@ -54,6 +55,7 @@
 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
@@ -66,6 +68,44 @@
 #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 @@
 			  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 @@
   /* 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 @@
 
 	  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 @@
   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 @@
 		      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 @@
 
   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 @@
 			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 @@
 }
 
 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 @@
   _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
@@ -886,18 +899,8 @@
 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 @@
 	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 @@
   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 @@
   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);
 

=== modified file 'src/fileio.c'
--- src/fileio.c	2013-01-23 20:07:28 +0000
+++ src/fileio.c	2013-01-24 08:02:16 +0000
@@ -82,6 +82,8 @@
 #endif
 
 #include "systime.h"
+#include <allocator.h>
+#include <careadlinkat.h>
 #include <stat-time.h>
 
 #ifdef HPUX
@@ -2758,6 +2760,29 @@
   return Qnil;
 }
 \f
+/* Relative to directory FD, return the symbolic link value of FILENAME.
+   On failure, return nil.  */
+Lisp_Object
+emacs_readlinkat (int fd, char const *filename)
+{
+  static struct allocator const emacs_norealloc_allocator =
+    { xmalloc, NULL, xfree, memory_full };
+  Lisp_Object val;
+  char readlink_buf[1024];
+  char *buf = careadlinkat (fd, filename, readlink_buf, sizeof readlink_buf,
+			    &emacs_norealloc_allocator, readlinkat);
+  if (!buf)
+    return Qnil;
+
+  val = build_string (buf);
+  if (buf[0] == '/' && strchr (buf, ':'))
+    val = concat2 (build_string ("/:"), val);
+  if (buf != readlink_buf)
+    xfree (buf);
+  val = DECODE_FILE (val);
+  return val;
+}
+
 DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
        doc: /* Return non-nil if file FILENAME is the name of a symbolic link.
 The value is the link target, as a string.
@@ -2768,9 +2793,6 @@
   (Lisp_Object filename)
 {
   Lisp_Object handler;
-  char *buf;
-  Lisp_Object val;
-  char readlink_buf[READLINK_BUFSIZE];
 
   CHECK_STRING (filename);
   filename = Fexpand_file_name (filename, Qnil);
@@ -2783,17 +2805,7 @@
 
   filename = ENCODE_FILE (filename);
 
-  buf = emacs_readlink (SSDATA (filename), readlink_buf);
-  if (! buf)
-    return Qnil;
-
-  val = build_string (buf);
-  if (buf[0] == '/' && strchr (buf, ':'))
-    val = concat2 (build_string ("/:"), val);
-  if (buf != readlink_buf)
-    xfree (buf);
-  val = DECODE_FILE (val);
-  return val;
+  return emacs_readlinkat (AT_FDCWD, SSDATA (filename));
 }
 
 DEFUN ("file-directory-p", Ffile_directory_p, Sfile_directory_p, 1, 1, 0,

=== modified file 'src/filelock.c'
--- src/filelock.c	2013-01-01 09:11:05 +0000
+++ src/filelock.c	2013-01-24 06:51:25 +0000
@@ -390,12 +390,14 @@
   lock_info_type local_owner;
   intmax_t n;
   char *at, *dot, *colon;
-  char readlink_buf[READLINK_BUFSIZE];
-  char *lfinfo = emacs_readlink (lfname, readlink_buf);
+  Lisp_Object lfinfo_object = emacs_readlinkat (AT_FDCWD, lfname);
+  char *lfinfo;
+  struct gcpro gcpro1;
 
   /* If nonexistent lock file, all is well; otherwise, got strange error. */
-  if (!lfinfo)
+  if (NILP (lfinfo_object))
     return errno == ENOENT ? 0 : -1;
+  lfinfo = SSDATA (lfinfo_object);
 
   /* Even if the caller doesn't want the owner info, we still have to
      read it to determine return value.  */
@@ -407,12 +409,9 @@
   at = strrchr (lfinfo, '@');
   dot = strrchr (lfinfo, '.');
   if (!at || !dot)
-    {
-      if (lfinfo != readlink_buf)
-	xfree (lfinfo);
-      return -1;
-    }
+    return -1;
   len = at - lfinfo;
+  GCPRO1 (lfinfo_object);
   owner->user = xmalloc (len + 1);
   memcpy (owner->user, lfinfo, len);
   owner->user[len] = 0;
@@ -445,8 +444,7 @@
   owner->host[len] = 0;
 
   /* We're done looking at the link info.  */
-  if (lfinfo != readlink_buf)
-    xfree (lfinfo);
+  UNGCPRO;
 
   /* On current host?  */
   if (STRINGP (Fsystem_name ())

=== modified file 'src/lisp.h'
--- src/lisp.h	2013-01-25 17:13:31 +0000
+++ src/lisp.h	2013-01-28 02:51:25 +0000
@@ -3294,6 +3294,7 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern bool internal_delete_file (Lisp_Object);
+extern Lisp_Object emacs_readlinkat (int, const char *);
 extern bool file_directory_p (const char *);
 extern bool file_accessible_directory_p (const char *);
 extern void init_fileio (void);
@@ -3566,8 +3567,6 @@
 extern int emacs_close (int);
 extern ptrdiff_t emacs_read (int, char *, ptrdiff_t);
 extern ptrdiff_t emacs_write (int, const char *, ptrdiff_t);
-enum { READLINK_BUFSIZE = 1024 };
-extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]);
 
 extern void unlock_all_files (void);
 extern void lock_file (Lisp_Object);

=== modified file 'src/sysdep.c'
--- src/sysdep.c	2013-01-13 20:03:01 +0000
+++ src/sysdep.c	2013-01-24 06:51:25 +0000
@@ -30,9 +30,7 @@
 #include <limits.h>
 #include <unistd.h>
 
-#include <allocator.h>
 #include <c-ctype.h>
-#include <careadlinkat.h>
 #include <ignore-value.h>
 #include <utimens.h>
 
@@ -2247,22 +2245,6 @@
 
   return (bytes_written);
 }
-
-static struct allocator const emacs_norealloc_allocator =
-  { xmalloc, NULL, xfree, memory_full };
-
-/* Get the symbolic link value of FILENAME.  Return a pointer to a
-   NUL-terminated string.  If readlink fails, return NULL and set
-   errno.  If the value fits in INITIAL_BUF, return INITIAL_BUF.
-   Otherwise, allocate memory and return a pointer to that memory.  If
-   memory allocation fails, diagnose and fail without returning.  If
-   successful, store the length of the symbolic link into *LINKLEN.  */
-char *
-emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE])
-{
-  return careadlinkat (AT_FDCWD, filename, initial_buf, READLINK_BUFSIZE,
-		       &emacs_norealloc_allocator, careadlinkatcwd);
-}
 \f
 /* Return a struct timeval that is roughly equivalent to T.
    Use the least timeval not less than T.

=== modified file 'src/w32.c'
--- src/w32.c	2013-01-28 14:41:08 +0000
+++ src/w32.c	2013-01-29 21:12:09 +0000
@@ -4194,6 +4194,30 @@
   return stat_worker (path, buf, 0);
 }
 
+int
+fstatat (int fd, char const *name, struct stat *st, int flags)
+{
+  /* Rely on a hack: an open directory is modeled as file descriptor 0.
+     This is good enough for the current usage in Emacs, but is fragile.
+
+     FIXME: Add proper support for fdopendir, fstatatat, readlinkat.
+     Gnulib does this and can serve as a model.  */
+  char fullname[MAX_PATH];
+
+  if (fd != AT_FDCWD)
+    {
+      if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+	  < 0)
+	{
+	  errno = ENAMETOOLONG;
+	  return -1;
+	}
+      name = fullname;
+    }
+
+  return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
+}
+
 /* Provide fstat and utime as well as stat for consistent handling of
    file timestamps. */
 int
@@ -4697,6 +4721,28 @@
   return retval;
 }
 
+ssize_t
+readlinkat (int fd, char const *name, char *buffer,
+	    size_t buffer_size)
+{
+  /* Rely on a hack: an open directory is modeled as file descriptor 0,
+     as in fstatat.  FIXME: Add proper support for readlinkat.  */
+  char fullname[MAX_PATH];
+
+  if (fd != AT_FDCWD)
+    {
+      if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+	  < 0)
+	{
+	  errno = ENAMETOOLONG;
+	  return -1;
+	}
+      name = fullname;
+    }
+
+  return readlink (name, buffer, buffer_size);
+}
+
 /* If FILE is a symlink, return its target (stored in a static
    buffer); otherwise return FILE.
 
@@ -5021,12 +5067,6 @@
   char linkname[MAX_PATH];
   ssize_t link_size;
 
-  if (fd != AT_FDCWD)
-    {
-      errno = EINVAL;
-      return NULL;
-    }
-
   link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
 
   if (link_size > 0)
@@ -5044,14 +5084,6 @@
   return NULL;
 }
 
-ssize_t
-careadlinkatcwd (int fd, char const *filename, char *buffer,
-                 size_t buffer_size)
-{
-  (void) fd;
-  return readlink (filename, buffer, buffer_size);
-}
-
 \f
 /* Support for browsing other processes and their attributes.  See
    process.c for the Lisp bindings.  */


^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-30  0:56       ` Paul Eggert
@ 2013-01-30 17:27         ` Eli Zaretskii
  2013-02-01  6:42           ` Paul Eggert
  2013-01-30 19:24         ` Stefan Monnier
  1 sibling, 1 reply; 9+ messages in thread
From: Eli Zaretskii @ 2013-01-30 17:27 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 13539

> Date: Tue, 29 Jan 2013 16:56:37 -0800
> From: Paul Eggert <eggert@cs.ucla.edu>
> CC: 13539@debbugs.gnu.org
> 
> But there's another approach: simply set fd = 0, and use 
> dir_pathname inside w32.c.  This should be good enough for what Emacs needs.
> Revised patch attached.

Works for me, thanks.





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-30  0:56       ` Paul Eggert
  2013-01-30 17:27         ` Eli Zaretskii
@ 2013-01-30 19:24         ` Stefan Monnier
  2013-01-30 20:24           ` Eli Zaretskii
  1 sibling, 1 reply; 9+ messages in thread
From: Stefan Monnier @ 2013-01-30 19:24 UTC (permalink / raw)
  To: Paul Eggert; +Cc: 13539

>> perhaps a further optimization would be to avoid the call to
>> file_name_completion_stat if completion-ignored-extensions is devoid
>> of elements that end in a slash.
> Yes, something like that might make sense.

Maybe.  But maybe not.  The default value of
completion-ignored-extensions includes elements that end in a slash, and
I think fairly few uses of try-completion rebind it to something else.


        Stefan





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-30 19:24         ` Stefan Monnier
@ 2013-01-30 20:24           ` Eli Zaretskii
  0 siblings, 0 replies; 9+ messages in thread
From: Eli Zaretskii @ 2013-01-30 20:24 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: 13539, eggert

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Cc: Eli Zaretskii <eliz@gnu.org>,  13539@debbugs.gnu.org
> Date: Wed, 30 Jan 2013 14:24:06 -0500
> 
> >> perhaps a further optimization would be to avoid the call to
> >> file_name_completion_stat if completion-ignored-extensions is devoid
> >> of elements that end in a slash.
> > Yes, something like that might make sense.
> 
> Maybe.  But maybe not.  The default value of
> completion-ignored-extensions includes elements that end in a slash, and
> I think fairly few uses of try-completion rebind it to something else.

I agree.  It just sounds excessive to invoke lstat/stat _twice_ just
to support this one feature.

Perhaps at least systems that have d_type could avoid one of these 2
calls.  (d_type can easily be added to the Windows emulation.)





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency.
  2013-01-30 17:27         ` Eli Zaretskii
@ 2013-02-01  6:42           ` Paul Eggert
  0 siblings, 0 replies; 9+ messages in thread
From: Paul Eggert @ 2013-02-01  6:42 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 13539-done

On 01/30/2013 09:27 AM, Eli Zaretskii wrote:
> Works for me, thanks.

OK, pushed into the trunk as bzr 111646,
and marking this bug as done.





^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2013-02-01  6:42 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-24  8:26 bug#13539: Use fdopendir, fstatat and readlinkat, for efficiency Paul Eggert
2013-01-24 17:28 ` Eli Zaretskii
2013-01-28  8:34   ` Paul Eggert
2013-01-28 14:37     ` Eli Zaretskii
2013-01-30  0:56       ` Paul Eggert
2013-01-30 17:27         ` Eli Zaretskii
2013-02-01  6:42           ` Paul Eggert
2013-01-30 19:24         ` Stefan Monnier
2013-01-30 20:24           ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).