emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] /srv/bzr/emacs/trunk r111646: Use fdopendir, fstatat and r


From: Paul Eggert
Subject: [Emacs-diffs] /srv/bzr/emacs/trunk r111646: Use fdopendir, fstatat and readlinkat, for efficiency.
Date: Thu, 31 Jan 2013 22:30:51 -0800
User-agent: Bazaar (2.5.0)

------------------------------------------------------------
revno: 111646
fixes bug: http://debbugs.gnu.org/13539
committer: Paul Eggert <address@hidden>
branch nick: trunk
timestamp: Thu 2013-01-31 22:30:51 -0800
message:
  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/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.
  * admin/merge-gnulib (GNULIB_MODULES): Add fdopendir, fstatat, readlinkat.
  (GNULIB_TOOL_FLAGS): Do not avoid at-internal, openat-h.
  Avoid dup, open, opendir.
  * nt/inc/sys/stat.h (fstatat):
  * nt/inc/unistd.h (readlinkat): New decls.
  * src/conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): Remove.
  * src/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.
  * src/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.
  * src/filelock.c (current_lock_owner): Use emacs_readlinkat
  rather than emacs_readlink.
  * src/lisp.h (emacs_readlinkat): New decl.
  (READLINK_BUFSIZE, emacs_readlink): Remove.
  * src/sysdep.c: Do not include <allocator.h>, <careadlinkat.h>.
  (emacs_norealloc_allocator, emacs_readlink): Remove.
  This stuff is moved to fileio.c.
  * src/w32.c (fstatat, readlinkat): New functions.
  (careadlinkat): Don't check that fd == AT_FDCWD.
  (careadlinkatcwd): Remove; no longer needed.
added:
  lib/dirent.in.h
  lib/fdopendir.c
  lib/fstatat.c
  lib/openat-die.c
  lib/openat-priv.h
  lib/openat-proc.c
  lib/openat.h
  lib/readlinkat.c
  lib/save-cwd.c
  lib/save-cwd.h
  m4/dirent_h.m4
  m4/fdopendir.m4
  m4/fstatat.m4
  m4/readlinkat.m4
modified:
  .bzrignore
  ChangeLog
  admin/ChangeLog
  admin/merge-gnulib
  lib/Makefile.am
  lib/careadlinkat.c
  lib/careadlinkat.h
  lib/gnulib.mk
  m4/gnulib-comp.m4
  nt/ChangeLog
  nt/inc/sys/stat.h
  nt/inc/unistd.h
  src/ChangeLog
  src/conf_post.h
  src/dired.c
  src/fileio.c
  src/filelock.c
  src/lisp.h
  src/sysdep.c
  src/w32.c
=== modified file '.bzrignore'
--- a/.bzrignore        2013-01-23 23:49:46 +0000
+++ b/.bzrignore        2013-02-01 06:30:51 +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'
--- a/ChangeLog 2013-02-01 03:58:50 +0000
+++ b/ChangeLog 2013-02-01 06:30:51 +0000
@@ -1,3 +1,23 @@
+2013-02-01  Paul Eggert  <address@hidden>
+
+       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-02-01  Glenn Morris  <address@hidden>
 
        * make-dist: Only README files exist in lisp/ now, not README*.

=== modified file 'admin/ChangeLog'
--- a/admin/ChangeLog   2013-01-15 09:22:25 +0000
+++ b/admin/ChangeLog   2013-02-01 06:30:51 +0000
@@ -1,3 +1,10 @@
+2013-02-01  Paul Eggert  <address@hidden>
+
+       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  <address@hidden>
 
        * coccinelle/xsave.cocci: Semantic patch to adjust users of

=== modified file 'admin/merge-gnulib'
--- a/admin/merge-gnulib        2013-01-02 16:13:04 +0000
+++ b/admin/merge-gnulib        2013-02-01 06:30:51 +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'
--- a/lib/Makefile.am   2012-08-02 10:39:47 +0000
+++ b/lib/Makefile.am   2013-02-01 06:30:51 +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'
--- a/lib/careadlinkat.c        2013-01-01 09:11:05 +0000
+++ b/lib/careadlinkat.c        2013-02-01 06:30:51 +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'
--- a/lib/careadlinkat.h        2013-01-01 09:11:05 +0000
+++ b/lib/careadlinkat.h        2013-02-01 06:30:51 +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'
--- a/lib/dirent.in.h   1970-01-01 00:00:00 +0000
+++ b/lib/dirent.in.h   2013-02-01 06:30:51 +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 address@hidden@_DIRENT_H
+
+#if __GNUC__ >= 3
address@hidden@
+#endif
address@hidden@
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_DIRENT_H@
+# @INCLUDE_NEXT@ @NEXT_DIRENT_H@
+#endif
+
+#ifndef address@hidden@_DIRENT_H
+#define address@hidden@_DIRENT_H
+
+/* Get ino_t.  Needed on some systems, including glibc 2.8.  */
+#include <sys/types.h>
+
+#if address@hidden@
+/* 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 address@hidden@
+_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 address@hidden@
+_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 address@hidden@
+_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 address@hidden@
+_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 address@hidden@ || address@hidden@
+_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 address@hidden@
+_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 address@hidden@
+_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 /* address@hidden@_DIRENT_H */
+#endif /* address@hidden@_DIRENT_H */

=== added file 'lib/fdopendir.c'
--- a/lib/fdopendir.c   1970-01-01 00:00:00 +0000
+++ b/lib/fdopendir.c   2013-02-01 06:30:51 +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'
--- a/lib/fstatat.c     1970-01-01 00:00:00 +0000
+++ b/lib/fstatat.c     2013-02-01 06:30:51 +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'
--- a/lib/gnulib.mk     2013-01-04 02:17:49 +0000
+++ b/lib/gnulib.mk     2013-02-01 06:30:51 +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 address@hidden $@ && \
+       { 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; \
+       } > address@hidden && \
+       mv address@hidden $@
+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'
--- a/lib/openat-die.c  1970-01-01 00:00:00 +0000
+++ b/lib/openat-die.c  2013-02-01 06:30:51 +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'
--- a/lib/openat-priv.h 1970-01-01 00:00:00 +0000
+++ b/lib/openat-priv.h 2013-02-01 06:30:51 +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'
--- a/lib/openat-proc.c 1970-01-01 00:00:00 +0000
+++ b/lib/openat-proc.c 2013-02-01 06:30:51 +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'
--- a/lib/openat.h      1970-01-01 00:00:00 +0000
+++ b/lib/openat.h      2013-02-01 06:30:51 +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'
--- a/lib/readlinkat.c  1970-01-01 00:00:00 +0000
+++ b/lib/readlinkat.c  2013-02-01 06:30:51 +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'
--- a/lib/save-cwd.c    1970-01-01 00:00:00 +0000
+++ b/lib/save-cwd.c    2013-02-01 06:30:51 +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'
--- a/lib/save-cwd.h    1970-01-01 00:00:00 +0000
+++ b/lib/save-cwd.h    2013-02-01 06:30:51 +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'
--- a/m4/dirent_h.m4    1970-01-01 00:00:00 +0000
+++ b/m4/dirent_h.m4    2013-02-01 06:30:51 +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'
--- a/m4/fdopendir.m4   1970-01-01 00:00:00 +0000
+++ b/m4/fdopendir.m4   2013-02-01 06:30:51 +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'
--- a/m4/fstatat.m4     1970-01-01 00:00:00 +0000
+++ b/m4/fstatat.m4     2013-02-01 06:30:51 +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'
--- a/m4/gnulib-comp.m4 2013-01-04 02:17:49 +0000
+++ b/m4/gnulib-comp.m4 2013-02-01 06:30:51 +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'
--- a/m4/readlinkat.m4  1970-01-01 00:00:00 +0000
+++ b/m4/readlinkat.m4  2013-02-01 06:30:51 +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'
--- a/nt/ChangeLog      2013-01-28 14:41:08 +0000
+++ b/nt/ChangeLog      2013-02-01 06:30:51 +0000
@@ -1,3 +1,9 @@
+2013-02-01  Paul Eggert  <address@hidden>
+
+       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  <address@hidden>
 
        * inc/dirent.h (opendir): Update prototype.

=== modified file 'nt/inc/sys/stat.h'
--- a/nt/inc/sys/stat.h 2013-01-02 16:13:04 +0000
+++ b/nt/inc/sys/stat.h 2013-02-01 06:30:51 +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'
--- a/nt/inc/unistd.h   2013-01-11 09:33:54 +0000
+++ b/nt/inc/unistd.h   2013-02-01 06:30:51 +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'
--- a/src/ChangeLog     2013-01-31 17:07:57 +0000
+++ b/src/ChangeLog     2013-02-01 06:30:51 +0000
@@ -1,3 +1,34 @@
+2013-02-01  Paul Eggert  <address@hidden>
+
+       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-31  Glenn Morris  <address@hidden>
 
        * fileio.c (choose_write_coding_system): Make it callable from Lisp.

=== modified file 'src/conf_post.h'
--- a/src/conf_post.h   2013-01-02 16:13:04 +0000
+++ b/src/conf_post.h   2013-02-01 06:30:51 +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'
--- a/src/dired.c       2013-01-17 06:29:40 +0000
+++ b/src/dired.c       2013-02-01 06:30:51 +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);
 
 /* 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;
 }
 
@@ -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'
--- a/src/fileio.c      2013-01-31 16:53:37 +0000
+++ b/src/fileio.c      2013-02-01 06:30:51 +0000
@@ -82,6 +82,8 @@
 #endif
 
 #include "systime.h"
+#include <allocator.h>
+#include <careadlinkat.h>
 #include <stat-time.h>
 
 #ifdef HPUX
@@ -2759,6 +2761,29 @@
   return Qnil;
 }
 
+/* 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.
@@ -2769,9 +2794,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);
@@ -2784,17 +2806,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'
--- a/src/filelock.c    2013-01-01 09:11:05 +0000
+++ b/src/filelock.c    2013-02-01 06:30:51 +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'
--- a/src/lisp.h        2013-01-25 17:13:31 +0000
+++ b/src/lisp.h        2013-02-01 06:30:51 +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'
--- a/src/sysdep.c      2013-01-13 20:03:01 +0000
+++ b/src/sysdep.c      2013-02-01 06:30:51 +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);
-}
 
 /* Return a struct timeval that is roughly equivalent to T.
    Use the least timeval not less than T.

=== modified file 'src/w32.c'
--- a/src/w32.c 2013-01-30 08:07:37 +0000
+++ b/src/w32.c 2013-02-01 06:30:51 +0000
@@ -4274,6 +4274,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
@@ -4816,6 +4840,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.
 
@@ -5168,12 +5214,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)
@@ -5191,14 +5231,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);
-}
-
 
 /* Support for browsing other processes and their attributes.  See
    process.c for the Lisp bindings.  */


reply via email to

[Prev in Thread] Current Thread [Next in Thread]