Correct SunOS and Solaris version number notation to match Sun's usage.
[gnulib.git] / lib / stat.c
index 00b956a..284da0c 100644 (file)
@@ -1,7 +1,10 @@
-/* Work around the bug in some systems whereby stat succeeds when
-   given the zero-length file name argument.  The stat from SunOS4.1.4
-   has this bug.
-   Copyright (C) 1997 Free Software Foundation, Inc.
+/* Work around the bug in some systems whereby stat/lstat succeeds when
+   given the zero-length file name argument.  The stat/lstat from SunOS 4.1.4
+   has this bug.  Also work around a deficiency in Solaris systems (up to at
+   least Solaris 9) regarding the semantics of `lstat ("symlink/", sbuf).'
+
+   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 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
 
 #include <config.h>
 
-/* Disable the definition of stat to rpl_stat (from config.h) in this
-   file.  Otherwise, we'd get conflicting prototypes for rpl_stat on
-   most systems.  */
-#undef stat
-
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>
 #ifndef errno
 extern int errno;
 #endif
+#if defined LSTAT && ! LSTAT_FOLLOWS_SLASHED_SYMLINK
+# include <string.h>
+
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+
+# ifdef STAT_MACROS_BROKEN
+#  undef S_ISLNK
+# endif
+
+# ifndef S_ISLNK
+#  ifdef S_IFLNK
+#   define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#  else
+#   define S_ISLNK(m) 0
+#  endif
+# endif
+
+# ifndef HAVE_DECL_FREE
+"this configure-time declaration test was not run"
+# endif
+# if !HAVE_DECL_FREE
+void free ();
+# endif
+
+# include "xalloc.h"
+
+/* lstat works differently on Linux and Solaris systems.  POSIX (see
+   `pathname resolution' in the glossary) requires that programs like `ls'
+   take into consideration the fact that FILE has a trailing slash when
+   FILE is a symbolic link.  On Linux systems, the lstat function already
+   has the desired semantics (in treating `lstat("symlink/",sbuf)' just like
+   `lstat("symlink/.",sbuf)', but on Solaris it does not.
+
+   If FILE has a trailing slash and specifies a symbolic link,
+   then append a `.' to FILE and call lstat a second time.  */
+
+static int
+slash_aware_lstat (const char *file, struct stat *sbuf)
+{
+  size_t len;
+  char *new_file;
+
+  int lstat_result = lstat (file, sbuf);
 
-/* FIXME: describe.  */
+  if (lstat_result != 0 || !S_ISLNK (sbuf->st_mode))
+    return lstat_result;
+
+  len = strlen (file);
+  if (file[len - 1] != '/')
+    return lstat_result;
+
+  /* FILE refers to a symbolic link and the name ends with a slash.
+     Append a `.' to FILE and repeat the lstat call.  */
+
+  /* Add one for the `.' we'll append, and one more for the trailing NUL.  */
+  new_file = xmalloc (len + 1 + 1);
+  memcpy (new_file, file, len);
+  new_file[len] = '.';
+  new_file[len + 1] = 0;
+
+  lstat_result = lstat (new_file, sbuf);
+  free (new_file);
+
+  return lstat_result;
+}
+#endif /* LSTAT && ! LSTAT_FOLLOWS_SLASHED_SYMLINK */
+
+/* This is a wrapper for stat/lstat.
+   If FILE is the empty string, fail with errno == ENOENT.
+   Otherwise, return the result of calling the real stat/lstat.
+
+   This works around the bug in some systems whereby stat/lstat succeeds when
+   given the zero-length file name argument.  The stat/lstat from SunOS 4.1.4
+   has this bug.  */
+
+/* This function also provides a version of lstat with consistent semantics
+   when FILE specifies a symbolic link and has a trailing slash.  */
+
+#ifdef LSTAT
+# define rpl_xstat rpl_lstat
+# if ! LSTAT_FOLLOWS_SLASHED_SYMLINK
+#  define xstat_return_val(F, S) slash_aware_lstat (F, S)
+# else
+#  define xstat_return_val(F, S) lstat (F, S)
+# endif
+#else
+# define rpl_xstat rpl_stat
+# define xstat_return_val(F, S) stat (F, S)
+#endif
 
 int
-rpl_stat (file, sbuf)
-     const char *file;
-     struct stat *sbuf;
+rpl_xstat (const char *file, struct stat *sbuf)
 {
   if (file && *file == 0)
     {
-      errno = EINVAL;
+      errno = ENOENT;
       return -1;
     }
 
-  return stat (file, sbuf);
+  return xstat_return_val (file, sbuf);
 }