* lib/unistd.in.h (_gl_directory_name): New prototype.
* lib/fchdir.c (_gl_directory_name): Implement it.
(fchdir): Use it to simplify implementation.
* lib/fdopendir.c (fdopendir) [REPLACE_FCHDIR]: Use metadata from
fchdir, when available, to avoid calling [f]chdir().
Signed-off-by: Eric Blake <ebb9@byu.net>
2009-09-02 Eric Blake <ebb9@byu.net>
+ fdopendir: optimize on mingw
+ * lib/unistd.in.h (_gl_directory_name): New prototype.
+ * lib/fchdir.c (_gl_directory_name): Implement it.
+ (fchdir): Use it to simplify implementation.
+ * lib/fdopendir.c (fdopendir) [REPLACE_FCHDIR]: Use metadata from
+ fchdir, when available, to avoid calling [f]chdir().
+
fdopendir: split into its own module
* lib/openat.c (fdopendir): Move...
* lib/fdopendir.c: ...into new file.
return newfd;
}
+/* If FD is currently visiting a directory, then return the name of
+ that directory. Otherwise, return NULL and set errno. */
+const char *
+_gl_directory_name (int fd)
+{
+ if (0 <= fd && fd < dirs_allocated && dirs[fd].name != NULL)
+ return dirs[fd].name;
+ /* At this point, fd is either invalid, or open but not a directory.
+ If dup2 fails, errno is correctly EBADF. */
+ if (0 <= fd)
+ {
+ if (dup2 (fd, fd) == fd)
+ errno = ENOTDIR;
+ }
+ else
+ errno = EBADF;
+ return NULL;
+}
+
/* Return stat information about FD in STATBUF. Needed when
rpl_open() used a dummy file to work around an open() that can't
normally visit directories. */
int
fchdir (int fd)
{
- if (fd >= 0 && fd < dirs_allocated && dirs[fd].name != NULL)
- return chdir (dirs[fd].name);
- /* At this point, fd is either invalid, or open but not a directory.
- If dup2 fails, errno is correctly EBADF. */
- if (0 <= fd)
- {
- if (dup2 (fd, fd) == fd)
- errno = ENOTDIR;
- }
- else
- errno = EBADF;
- return -1;
+ const char *name = _gl_directory_name (fd);
+ return name ? chdir (name) : -1;
}
/* Replacement for Solaris' function by the same name.
<http://www.google.com/search?q=fdopendir+site:docs.sun.com>
First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
- that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
+ 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.
Otherwise, this function works just like Solaris' fdopendir.
DIR *
fdopendir (int fd)
{
- struct saved_cwd saved_cwd;
int saved_errno;
DIR *dir;
save_cwd/restore_cwd. */
if (! dir && EXPECTED_ERRNO (saved_errno))
{
+#if REPLACE_FCHDIR
+ const char *name = _gl_directory_name (fd);
+ if (name)
+ dir = opendir (name);
+ saved_errno = errno;
+#else /* !REPLACE_FCHDIR */
+ struct saved_cwd saved_cwd;
if (save_cwd (&saved_cwd) != 0)
openat_save_fail (errno);
}
free_cwd (&saved_cwd);
+#endif /* !REPLACE_FCHDIR */
}
if (dir)
extern int _gl_register_fd (int fd, const char *filename);
extern void _gl_unregister_fd (int fd);
extern int _gl_register_dup (int oldfd, int newfd);
+extern const char *_gl_directory_name (int fd);
# endif
#elif defined GNULIB_POSIXCHECK