Blame gnulib/lib/lstat.c

Packit Service a2ae7a
/* Work around a bug of lstat on some systems
Packit Service a2ae7a
Packit Service a2ae7a
   Copyright (C) 1997-2006, 2008-2019 Free Software Foundation, Inc.
Packit Service a2ae7a
Packit Service a2ae7a
   This program is free software: you can redistribute it and/or modify
Packit Service a2ae7a
   it under the terms of the GNU Lesser General Public License as published by
Packit Service a2ae7a
   the Free Software Foundation; either version 2.1 of the License, or
Packit Service a2ae7a
   (at your option) any later version.
Packit Service a2ae7a
Packit Service a2ae7a
   This program is distributed in the hope that it will be useful,
Packit Service a2ae7a
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2ae7a
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2ae7a
   GNU Lesser General Public License for more details.
Packit Service a2ae7a
Packit Service a2ae7a
   You should have received a copy of the GNU Lesser General Public License
Packit Service a2ae7a
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit Service a2ae7a
Packit Service a2ae7a
/* written by Jim Meyering */
Packit Service a2ae7a
Packit Service a2ae7a
/* If the user's config.h happens to include <sys/stat.h>, let it include only
Packit Service a2ae7a
   the system's <sys/stat.h> here, so that orig_lstat doesn't recurse to
Packit Service a2ae7a
   rpl_lstat.  */
Packit Service a2ae7a
#define __need_system_sys_stat_h
Packit Service a2ae7a
#include <config.h>
Packit Service a2ae7a
Packit Service a2ae7a
#if !HAVE_LSTAT
Packit Service a2ae7a
/* On systems that lack symlinks, our replacement <sys/stat.h> already
Packit Service a2ae7a
   defined lstat as stat, so there is nothing further to do other than
Packit Service a2ae7a
   avoid an empty file.  */
Packit Service a2ae7a
typedef int dummy;
Packit Service a2ae7a
#else /* HAVE_LSTAT */
Packit Service a2ae7a
Packit Service a2ae7a
/* Get the original definition of lstat.  It might be defined as a macro.  */
Packit Service a2ae7a
# include <sys/types.h>
Packit Service a2ae7a
# include <sys/stat.h>
Packit Service a2ae7a
# undef __need_system_sys_stat_h
Packit Service a2ae7a
Packit Service a2ae7a
static int
Packit Service a2ae7a
orig_lstat (const char *filename, struct stat *buf)
Packit Service a2ae7a
{
Packit Service a2ae7a
  return lstat (filename, buf);
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
/* Specification.  */
Packit Service a2ae7a
# ifdef __osf__
Packit Service a2ae7a
/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
Packit Service a2ae7a
   eliminates this include because of the preliminary #include <sys/stat.h>
Packit Service a2ae7a
   above.  */
Packit Service a2ae7a
#  include "sys/stat.h"
Packit Service a2ae7a
# else
Packit Service a2ae7a
#  include <sys/stat.h>
Packit Service a2ae7a
# endif
Packit Service a2ae7a
Packit Service a2ae7a
# include "stat-time.h"
Packit Service a2ae7a
Packit Service a2ae7a
# include <string.h>
Packit Service a2ae7a
# include <errno.h>
Packit Service a2ae7a
Packit Service a2ae7a
/* lstat works differently on Linux and Solaris systems.  POSIX (see
Packit Service a2ae7a
   "pathname resolution" in the glossary) requires that programs like
Packit Service a2ae7a
   'ls' take into consideration the fact that FILE has a trailing slash
Packit Service a2ae7a
   when FILE is a symbolic link.  On Linux and Solaris 10 systems, the
Packit Service a2ae7a
   lstat function already has the desired semantics (in treating
Packit Service a2ae7a
   'lstat ("symlink/", sbuf)' just like 'lstat ("symlink/.", sbuf)',
Packit Service a2ae7a
   but on Solaris 9 and earlier it does not.
Packit Service a2ae7a
Packit Service a2ae7a
   If FILE has a trailing slash and specifies a symbolic link,
Packit Service a2ae7a
   then use stat() to get more info on the referent of FILE.
Packit Service a2ae7a
   If the referent is a non-directory, then set errno to ENOTDIR
Packit Service a2ae7a
   and return -1.  Otherwise, return stat's result.  */
Packit Service a2ae7a
Packit Service a2ae7a
int
Packit Service a2ae7a
rpl_lstat (const char *file, struct stat *sbuf)
Packit Service a2ae7a
{
Packit Service a2ae7a
  int result = orig_lstat (file, sbuf);
Packit Service a2ae7a
Packit Service a2ae7a
  /* This replacement file can blindly check against '/' rather than
Packit Service a2ae7a
     using the ISSLASH macro, because all platforms with '\\' either
Packit Service a2ae7a
     lack symlinks (mingw) or have working lstat (cygwin) and thus do
Packit Service a2ae7a
     not compile this file.  0 len should have already been filtered
Packit Service a2ae7a
     out above, with a failure return of ENOENT.  */
Packit Service a2ae7a
  if (result == 0)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      if (S_ISDIR (sbuf->st_mode) || file[strlen (file) - 1] != '/')
Packit Service a2ae7a
        result = stat_time_normalize (result, sbuf);
Packit Service a2ae7a
      else
Packit Service a2ae7a
        {
Packit Service a2ae7a
          /* At this point, a trailing slash is permitted only on
Packit Service a2ae7a
             symlink-to-dir; but it should have found information on the
Packit Service a2ae7a
             directory, not the symlink.  Call 'stat' to get info about the
Packit Service a2ae7a
             link's referent.  Our replacement stat guarantees valid results,
Packit Service a2ae7a
             even if the symlink is not pointing to a directory.  */
Packit Service a2ae7a
          if (!S_ISLNK (sbuf->st_mode))
Packit Service a2ae7a
            {
Packit Service a2ae7a
              errno = ENOTDIR;
Packit Service a2ae7a
              return -1;
Packit Service a2ae7a
            }
Packit Service a2ae7a
          result = stat (file, sbuf);
Packit Service a2ae7a
        }
Packit Service a2ae7a
    }
Packit Service a2ae7a
  return result;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
#endif /* HAVE_LSTAT */