Mercurial > hg > octave-lojdl > gnulib-hg
changeset 12036:1762604ec0a7
stat: new module, for mingw bug
Depending on the current directory, either stat(".",buf) or
stat("./",buf) would fail on mingw.
* 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.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Tue, 15 Sep 2009 07:11:40 -0600 |
parents | 2898ee238452 |
children | eb6b9da995d7 |
files | ChangeLog MODULES.html.sh doc/posix-functions/stat.texi lib/openat.c lib/stat.c lib/sys_stat.in.h m4/stat.m4 m4/sys_stat_h.m4 modules/openat modules/stat modules/stat-tests modules/sys_stat tests/test-stat.c |
diffstat | 13 files changed, 300 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +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
--- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2331,6 +2331,7 @@ func_module socket func_module spawn func_module sprintf-posix + func_module stat func_module strdup-posix func_module string func_module strings
--- a/doc/posix-functions/stat.texi +++ b/doc/posix-functions/stat.texi @@ -4,10 +4,14 @@ 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: @@ -19,4 +23,12 @@ @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
--- a/lib/openat.c +++ b/lib/openat.c @@ -157,6 +157,24 @@ 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"). @@ -167,7 +185,7 @@ #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
new file mode 100644 --- /dev/null +++ b/lib/stat.c @@ -0,0 +1,78 @@ +/* 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; +}
--- a/lib/sys_stat.in.h +++ b/lib/sys_stat.in.h @@ -302,6 +302,22 @@ 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@
new file mode 100644 --- /dev/null +++ b/m4/stat.m4 @@ -0,0 +1,31 @@ +# 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 +])
--- a/m4/sys_stat_h.m4 +++ b/m4/sys_stat_h.m4 @@ -1,4 +1,4 @@ -# 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, @@ -56,6 +56,7 @@ 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]) @@ -67,4 +68,5 @@ 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]) ])
--- a/modules/openat +++ b/modules/openat @@ -22,6 +22,7 @@ fcntl-h fdopendir gettext-h +inline intprops lchown lstat
new file mode 100644 --- /dev/null +++ b/modules/stat @@ -0,0 +1,26 @@ +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
new file mode 100644 --- /dev/null +++ b/modules/stat-tests @@ -0,0 +1,12 @@ +Files: +tests/test-stat.c + +Depends-on: +pathmax +same-inode + +configure.ac: + +Makefile.am: +TESTS += test-stat +check_PROGRAMS += test-stat
--- a/modules/sys_stat +++ b/modules/sys_stat @@ -33,6 +33,7 @@ -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' \ @@ -44,6 +45,7 @@ -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 && \
new file mode 100644 --- /dev/null +++ b/tests/test-stat.c @@ -0,0 +1,81 @@ +/* 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; +}