Blame gl/lstat.c

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