Blame gnulib/lib/stat.c

Packit eba2e2
/* Work around platform bugs in stat.
Packit eba2e2
   Copyright (C) 2009-2014 Free Software Foundation, Inc.
Packit eba2e2
Packit eba2e2
   This program is free software: you can redistribute it and/or modify
Packit eba2e2
   it under the terms of the GNU General Public License as published by
Packit eba2e2
   the Free Software Foundation; either version 3 of the License, or
Packit eba2e2
   (at your option) any later version.
Packit eba2e2
Packit eba2e2
   This program is distributed in the hope that it will be useful,
Packit eba2e2
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eba2e2
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit eba2e2
   GNU General Public License for more details.
Packit eba2e2
Packit eba2e2
   You should have received a copy of the GNU General Public License
Packit eba2e2
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit eba2e2
Packit eba2e2
/* written by Eric Blake */
Packit eba2e2
Packit eba2e2
/* If the user's config.h happens to include <sys/stat.h>, let it include only
Packit eba2e2
   the system's <sys/stat.h> here, so that orig_stat doesn't recurse to
Packit eba2e2
   rpl_stat.  */
Packit eba2e2
#define __need_system_sys_stat_h
Packit eba2e2
#include <config.h>
Packit eba2e2
Packit eba2e2
/* Get the original definition of stat.  It might be defined as a macro.  */
Packit eba2e2
#include <sys/types.h>
Packit eba2e2
#include <sys/stat.h>
Packit eba2e2
#undef __need_system_sys_stat_h
Packit eba2e2
Packit eba2e2
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit eba2e2
# if _GL_WINDOWS_64_BIT_ST_SIZE
Packit eba2e2
#  undef stat /* avoid warning on mingw64 with _FILE_OFFSET_BITS=64 */
Packit eba2e2
#  define stat _stati64
Packit eba2e2
#  define REPLACE_FUNC_STAT_DIR 1
Packit eba2e2
#  undef REPLACE_FUNC_STAT_FILE
Packit eba2e2
# elif REPLACE_FUNC_STAT_FILE
Packit eba2e2
/* mingw64 has a broken stat() function, based on _stat(), in libmingwex.a.
Packit eba2e2
   Bypass it.  */
Packit eba2e2
#  define stat _stat
Packit eba2e2
#  define REPLACE_FUNC_STAT_DIR 1
Packit eba2e2
#  undef REPLACE_FUNC_STAT_FILE
Packit eba2e2
# endif
Packit eba2e2
#endif
Packit eba2e2
Packit eba2e2
static int
Packit eba2e2
orig_stat (const char *filename, struct stat *buf)
Packit eba2e2
{
Packit eba2e2
  return stat (filename, buf);
Packit eba2e2
}
Packit eba2e2
Packit eba2e2
/* Specification.  */
Packit eba2e2
/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
Packit eba2e2
   eliminates this include because of the preliminary #include <sys/stat.h>
Packit eba2e2
   above.  */
Packit eba2e2
#include "sys/stat.h"
Packit eba2e2
Packit eba2e2
#include <errno.h>
Packit eba2e2
#include <limits.h>
Packit eba2e2
#include <stdbool.h>
Packit eba2e2
#include <string.h>
Packit eba2e2
#include "dosname.h"
Packit eba2e2
#include "verify.h"
Packit eba2e2
Packit eba2e2
#if REPLACE_FUNC_STAT_DIR
Packit eba2e2
# include "pathmax.h"
Packit eba2e2
  /* The only known systems where REPLACE_FUNC_STAT_DIR is needed also
Packit eba2e2
     have a constant PATH_MAX.  */
Packit eba2e2
# ifndef PATH_MAX
Packit eba2e2
#  error "Please port this replacement to your platform"
Packit eba2e2
# endif
Packit eba2e2
#endif
Packit eba2e2
Packit eba2e2
/* Store information about NAME into ST.  Work around bugs with
Packit eba2e2
   trailing slashes.  Mingw has other bugs (such as st_ino always
Packit eba2e2
   being 0 on success) which this wrapper does not work around.  But
Packit eba2e2
   at least this implementation provides the ability to emulate fchdir
Packit eba2e2
   correctly.  */
Packit eba2e2
Packit eba2e2
int
Packit eba2e2
rpl_stat (char const *name, struct stat *st)
Packit eba2e2
{
Packit eba2e2
  int result = orig_stat (name, st);
Packit eba2e2
#if REPLACE_FUNC_STAT_FILE
Packit eba2e2
  /* Solaris 9 mistakenly succeeds when given a non-directory with a
Packit eba2e2
     trailing slash.  */
Packit eba2e2
  if (result == 0 && !S_ISDIR (st->st_mode))
Packit eba2e2
    {
Packit eba2e2
      size_t len = strlen (name);
Packit eba2e2
      if (ISSLASH (name[len - 1]))
Packit eba2e2
        {
Packit eba2e2
          errno = ENOTDIR;
Packit eba2e2
          return -1;
Packit eba2e2
        }
Packit eba2e2
    }
Packit eba2e2
#endif /* REPLACE_FUNC_STAT_FILE */
Packit eba2e2
#if REPLACE_FUNC_STAT_DIR
Packit eba2e2
Packit eba2e2
  if (result == -1 && errno == ENOENT)
Packit eba2e2
    {
Packit eba2e2
      /* Due to mingw's oddities, there are some directories (like
Packit eba2e2
         c:\) where stat() only succeeds with a trailing slash, and
Packit eba2e2
         other directories (like c:\windows) where stat() only
Packit eba2e2
         succeeds without a trailing slash.  But we want the two to be
Packit eba2e2
         synonymous, since chdir() manages either style.  Likewise, Mingw also
Packit eba2e2
         reports ENOENT for names longer than PATH_MAX, when we want
Packit eba2e2
         ENAMETOOLONG, and for stat("file/"), when we want ENOTDIR.
Packit eba2e2
         Fortunately, mingw PATH_MAX is small enough for stack
Packit eba2e2
         allocation.  */
Packit eba2e2
      char fixed_name[PATH_MAX + 1] = {0};
Packit eba2e2
      size_t len = strlen (name);
Packit eba2e2
      bool check_dir = false;
Packit eba2e2
      verify (PATH_MAX <= 4096);
Packit eba2e2
      if (PATH_MAX <= len)
Packit eba2e2
        errno = ENAMETOOLONG;
Packit eba2e2
      else if (len)
Packit eba2e2
        {
Packit eba2e2
          strcpy (fixed_name, name);
Packit eba2e2
          if (ISSLASH (fixed_name[len - 1]))
Packit eba2e2
            {
Packit eba2e2
              check_dir = true;
Packit eba2e2
              while (len && ISSLASH (fixed_name[len - 1]))
Packit eba2e2
                fixed_name[--len] = '\0';
Packit eba2e2
              if (!len)
Packit eba2e2
                fixed_name[0] = '/';
Packit eba2e2
            }
Packit eba2e2
          else
Packit eba2e2
            fixed_name[len++] = '/';
Packit eba2e2
          result = orig_stat (fixed_name, st);
Packit eba2e2
          if (result == 0 && check_dir && !S_ISDIR (st->st_mode))
Packit eba2e2
            {
Packit eba2e2
              result = -1;
Packit eba2e2
              errno = ENOTDIR;
Packit eba2e2
            }
Packit eba2e2
        }
Packit eba2e2
    }
Packit eba2e2
#endif /* REPLACE_FUNC_STAT_DIR */
Packit eba2e2
  return result;
Packit eba2e2
}