+2009-09-19 Eric Blake <ebb9@byu.net>
+
+ stat: new module, for mingw bug
+ * modules/stat: New file.
+ * lib/stat.c: Likewise.
+ * m4/stat.m4 (gl_FUNC_STAT): Likewise.
+ * m4/sys_stat_h.m4 (gl_SYS_STAT_H_DEFAULTS): Add witnesses.
+ * modules/sys_stat (Makefile.am): Use them.
+ * lib/sys_stat.in.h (stat): Declare replacement.
+ * lib/openat.c (fstatat): Deal with lstat and stat being function
+ macros.
+ * modules/openat (Depends-on): Add inline.
+ * MODULES.html.sh (systems lacking POSIX:2008): Mention module.
+ * doc/posix-functions/stat.texi (stat): Likewise.
+ * modules/stat-tests: New test.
+ * tests/test-stat.c: Likewise.
+
2009-09-19 Jim Meyering <meyering@redhat.com>
syntax-check: detect unnecessary inclusion of canonicalize.h
func_module socket
func_module spawn
func_module sprintf-posix
+ func_module stat
func_module strdup-posix
func_module string
func_module strings
POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/stat.html}
-Gnulib module: ---
+Gnulib module: stat
Portability problems fixed by Gnulib:
@itemize
+@item
+On some platforms, @code{stat(".",buf)} and @code{stat("./",buf)} give
+different results:
+mingw.
@end itemize
Portability problems not fixed by Gnulib:
@item
Cygwin's @code{stat} function sometimes sets @code{errno} to @code{EACCES} when
@code{ENOENT} would be more appropriate.
+@item
+On Windows platforms (excluding Cygwin), @code{st_ino} is always 0.
+@item
+Because of the definition of @code{struct stat}, it is not possible to
+portably replace @code{stat} via an object-like macro. Therefore,
+expressions such as @code{(islnk ? lstat : stat) (name, buf)} are not
+portable, and should instead be written @code{islnk ? lstat (name,
+buf) : stat (name, buf)}.
@end itemize
return needs_fchdir;
}
+/* 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 inline 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").
#define AT_FUNC_NAME fstatat
#define AT_FUNC_F1 lstat
-#define AT_FUNC_F2 stat
+#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
--- /dev/null
+/* Work around platform bugs in stat.
+ Copyright (C) 2009 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 <sys/stat.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <string.h>
+
+#undef stat
+
+/* For now, mingw is the only known platform where stat(".") and
+ stat("./") give different results. Mingw stat has other bugs (such
+ as st_ino always being 0 on success) which this wrapper does not
+ work around. But at least this implementation provides the ability
+ to emulate fchdir correctly. */
+
+int
+rpl_stat (char const *name, struct stat *st)
+{
+ int result = stat (name, st);
+ if (result == -1 && errno == ENOENT)
+ {
+ /* Due to mingw's oddities, there are some directories (like
+ c:\) where stat() only succeeds with a trailing slash, and
+ other directories (like c:\windows) where stat() only
+ succeeds without a trailing slash. But we want the two to be
+ synonymous, since chdir() manages either style. Likewise, Mingw also
+ reports ENOENT for names longer than PATH_MAX, when we want
+ ENAMETOOLONG, and for stat("file/"), when we want ENOTDIR.
+ Fortunately, mingw PATH_MAX is small enough for stack
+ allocation. */
+ char fixed_name[PATH_MAX + 1] = {0};
+ size_t len = strlen (name);
+ bool check_dir = false;
+ if (PATH_MAX <= len)
+ errno = ENAMETOOLONG;
+ else if (len)
+ {
+ strcpy (fixed_name, name);
+ if (ISSLASH (fixed_name[len - 1]))
+ {
+ check_dir = true;
+ while (len && ISSLASH (fixed_name[len - 1]))
+ fixed_name[--len] = '\0';
+ if (!len)
+ fixed_name[0] = '/';
+ }
+ else
+ fixed_name[len++] = '/';
+ result = stat (fixed_name, st);
+ if (result == 0 && check_dir && !S_ISDIR (st->st_mode))
+ {
+ result = -1;
+ errno = ENOTDIR;
+ }
+ }
+ }
+ return result;
+}
lstat (p, b))
#endif
+#if @GNULIB_STAT@
+# if @REPLACE_STAT@
+/* We can't use the object-like #define stat rpl_stat, because of
+ struct stat. This means that rpl_stat will not be used if the user
+ does (stat)(a,b). Oh well. */
+# undef stat
+# define stat(name, st) rpl_stat (name, st)
+extern int stat (const char *name, struct stat *buf);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef stat
+# define stat(p,b) \
+ (GL_LINK_WARNING ("stat is unportable - " \
+ "use gnulib module stat for portability"), \
+ stat (p, b))
+#endif
#if @GNULIB_FCHMODAT@
# if !@HAVE_FCHMODAT@
--- /dev/null
+# serial 1
+
+# Copyright (C) 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_STAT],
+[
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_REQUIRE([gl_AC_DOS])
+ AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
+ dnl mingw is the only known platform where stat(".") and stat("./") differ
+ AC_CACHE_CHECK([whether stat handles trailing slashes],
+ [gl_cv_func_stat_works],
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <sys/stat.h>
+]], [[struct stat st; return stat (".", &st) != stat ("./", &st);]])],
+ [gl_cv_func_stat_works=yes], [gl_cv_func_stat_works=no],
+ [case $host_os in
+ mingw*) gl_cv_func_stat_works="guessing no";;
+ *) gl_cv_func_stat_works="guessing yes";;
+ esac])])
+ case $gl_cv_func_stat_works in
+ *yes) ;;
+ *) REPLACE_STAT=1
+ AC_LIBOBJ([stat]);;
+ esac
+])
-# sys_stat_h.m4 serial 15 -*- Autoconf -*-
+# sys_stat_h.m4 serial 16 -*- Autoconf -*-
dnl Copyright (C) 2006-2009 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
GNULIB_MKDIRAT=0; AC_SUBST([GNULIB_MKDIRAT])
GNULIB_MKFIFOAT=0; AC_SUBST([GNULIB_MKFIFOAT])
GNULIB_MKNODAT=0; AC_SUBST([GNULIB_MKNODAT])
+ GNULIB_STAT=0; AC_SUBST([GNULIB_STAT])
dnl Assume proper GNU behavior unless another module says otherwise.
HAVE_FCHMODAT=1; AC_SUBST([HAVE_FCHMODAT])
HAVE_FSTATAT=1; AC_SUBST([HAVE_FSTATAT])
REPLACE_FSTATAT=0; AC_SUBST([REPLACE_FSTATAT])
REPLACE_LSTAT=0; AC_SUBST([REPLACE_LSTAT])
REPLACE_MKDIR=0; AC_SUBST([REPLACE_MKDIR])
+ REPLACE_STAT=0; AC_SUBST([REPLACE_STAT])
])
fcntl-h
fdopendir
gettext-h
+inline
intprops
lchown
lstat
--- /dev/null
+Description:
+stat(): query file information
+
+Files:
+lib/stat.c
+m4/dos.m4
+m4/stat.m4
+
+Depends-on:
+stdbool
+sys_stat
+
+configure.ac:
+gl_FUNC_STAT
+gl_SYS_STAT_MODULE_INDICATOR([stat])
+
+Makefile.am:
+
+Include:
+<sys/stat.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+Eric Blake
--- /dev/null
+Files:
+tests/test-stat.c
+
+Depends-on:
+pathmax
+same-inode
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-stat
+check_PROGRAMS += test-stat
-e 's|@''GNULIB_MKDIRAT''@|$(GNULIB_MKDIRAT)|g' \
-e 's|@''GNULIB_MKFIFOAT''@|$(GNULIB_MKFIFOAT)|g' \
-e 's|@''GNULIB_MKNODAT''@|$(GNULIB_MKNODAT)|g' \
+ -e 's|@''GNULIB_STAT''@|$(GNULIB_STAT)|g' \
-e 's|@''HAVE_FCHMODAT''@|$(HAVE_FCHMODAT)|g' \
-e 's|@''HAVE_FSTATAT''@|$(HAVE_FSTATAT)|g' \
-e 's|@''HAVE_LCHMOD''@|$(HAVE_LCHMOD)|g' \
-e 's|@''REPLACE_FSTATAT''@|$(REPLACE_FSTATAT)|g' \
-e 's|@''REPLACE_LSTAT''@|$(REPLACE_LSTAT)|g' \
-e 's|@''REPLACE_MKDIR''@|$(REPLACE_MKDIR)|g' \
+ -e 's|@''REPLACE_STAT''@|$(REPLACE_STAT)|g' \
-e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \
< $(srcdir)/sys_stat.in.h; \
} > $@-t && \
--- /dev/null
+/* Tests of stat.
+ Copyright (C) 2009 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 <ebb9@byu.net>, 2009. */
+
+#include <config.h>
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pathmax.h"
+#include "same-inode.h"
+
+#define ASSERT(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ { \
+ fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+ fflush (stderr); \
+ abort (); \
+ } \
+ } \
+ while (0)
+
+#define BASE "test-stat.t"
+
+int
+main ()
+{
+ struct stat st1;
+ struct stat st2;
+ char cwd[PATH_MAX];
+
+ ASSERT (getcwd (cwd, PATH_MAX) == cwd);
+ ASSERT (stat (".", &st1) == 0);
+ ASSERT (stat ("./", &st2) == 0);
+ ASSERT (SAME_INODE (st1, st2));
+ ASSERT (stat (cwd, &st2) == 0);
+ ASSERT (SAME_INODE (st1, st2));
+ ASSERT (stat ("/", &st1) == 0);
+ ASSERT (stat ("///", &st2) == 0);
+ ASSERT (SAME_INODE (st1, st2));
+
+ errno = 0;
+ ASSERT (stat ("", &st1) == -1);
+ ASSERT (errno == ENOENT);
+ errno = 0;
+ ASSERT (stat ("nosuch", &st1) == -1);
+ ASSERT (errno == ENOENT);
+ errno = 0;
+ ASSERT (stat ("nosuch/", &st1) == -1);
+ ASSERT (errno == ENOENT);
+
+ ASSERT (close (creat (BASE "file", 0600)) == 0);
+ ASSERT (stat (BASE "file", &st1) == 0);
+ errno = 0;
+ ASSERT (stat (BASE "file/", &st1) == -1);
+ ASSERT (errno == ENOTDIR);
+ ASSERT (unlink (BASE "file") == 0);
+
+ return 0;
+}