Blame gettext-tools/gnulib-lib/fopen.c

Packit 5b56b6
/* Open a stream to a file.
Packit 5b56b6
   Copyright (C) 2007-2015 Free Software Foundation, Inc.
Packit 5b56b6
Packit 5b56b6
   This program is free software: you can redistribute it and/or modify
Packit 5b56b6
   it under the terms of the GNU General Public License as published by
Packit 5b56b6
   the Free Software Foundation; either version 3 of the License, or
Packit 5b56b6
   (at your option) any later version.
Packit 5b56b6
Packit 5b56b6
   This program is distributed in the hope that it will be useful,
Packit 5b56b6
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5b56b6
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 5b56b6
   GNU General Public License for more details.
Packit 5b56b6
Packit 5b56b6
   You should have received a copy of the GNU General Public License
Packit 5b56b6
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 5b56b6
Packit 5b56b6
/* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
Packit 5b56b6
Packit 5b56b6
/* If the user's config.h happens to include <stdio.h>, let it include only
Packit 5b56b6
   the system's <stdio.h> here, so that orig_fopen doesn't recurse to
Packit 5b56b6
   rpl_fopen.  */
Packit 5b56b6
#define __need_FILE
Packit 5b56b6
#include <config.h>
Packit 5b56b6
Packit 5b56b6
/* Get the original definition of fopen.  It might be defined as a macro.  */
Packit 5b56b6
#include <stdio.h>
Packit 5b56b6
#undef __need_FILE
Packit 5b56b6
Packit 5b56b6
static FILE *
Packit 5b56b6
orig_fopen (const char *filename, const char *mode)
Packit 5b56b6
{
Packit 5b56b6
  return fopen (filename, mode);
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* Specification.  */
Packit 5b56b6
/* Write "stdio.h" here, not <stdio.h>, otherwise OSF/1 5.1 DTK cc eliminates
Packit 5b56b6
   this include because of the preliminary #include <stdio.h> above.  */
Packit 5b56b6
#include "stdio.h"
Packit 5b56b6
Packit 5b56b6
#include <errno.h>
Packit 5b56b6
#include <fcntl.h>
Packit 5b56b6
#include <string.h>
Packit 5b56b6
#include <unistd.h>
Packit 5b56b6
#include <sys/types.h>
Packit 5b56b6
#include <sys/stat.h>
Packit 5b56b6
Packit 5b56b6
FILE *
Packit 5b56b6
rpl_fopen (const char *filename, const char *mode)
Packit 5b56b6
{
Packit 5b56b6
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit 5b56b6
  if (strcmp (filename, "/dev/null") == 0)
Packit 5b56b6
    filename = "NUL";
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#if FOPEN_TRAILING_SLASH_BUG
Packit 5b56b6
  /* If the filename ends in a slash and a mode that requires write access is
Packit 5b56b6
     specified, then fail.
Packit 5b56b6
     Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
Packit 5b56b6
     says that
Packit 5b56b6
       "A pathname that contains at least one non-slash character and that
Packit 5b56b6
        ends with one or more trailing slashes shall be resolved as if a
Packit 5b56b6
        single dot character ( '.' ) were appended to the pathname."
Packit 5b56b6
     and
Packit 5b56b6
       "The special filename dot shall refer to the directory specified by
Packit 5b56b6
        its predecessor."
Packit 5b56b6
     If the named file already exists as a directory, then if a mode that
Packit 5b56b6
     requires write access is specified, fopen() must fail because POSIX
Packit 5b56b6
     <http://www.opengroup.org/susv3/functions/fopen.html> says that it
Packit 5b56b6
     fails with errno = EISDIR in this case.
Packit 5b56b6
     If the named file does not exist or does not name a directory, then
Packit 5b56b6
     fopen() must fail since the file does not contain a '.' directory.  */
Packit 5b56b6
  {
Packit 5b56b6
    size_t len = strlen (filename);
Packit 5b56b6
    if (len > 0 && filename[len - 1] == '/')
Packit 5b56b6
      {
Packit 5b56b6
        int fd;
Packit 5b56b6
        struct stat statbuf;
Packit 5b56b6
        FILE *fp;
Packit 5b56b6
Packit 5b56b6
        if (mode[0] == 'w' || mode[0] == 'a')
Packit 5b56b6
          {
Packit 5b56b6
            errno = EISDIR;
Packit 5b56b6
            return NULL;
Packit 5b56b6
          }
Packit 5b56b6
Packit 5b56b6
        fd = open (filename, O_RDONLY);
Packit 5b56b6
        if (fd < 0)
Packit 5b56b6
          return NULL;
Packit 5b56b6
Packit 5b56b6
        if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
Packit 5b56b6
          {
Packit 5b56b6
            close (fd);
Packit 5b56b6
            errno = ENOTDIR;
Packit 5b56b6
            return NULL;
Packit 5b56b6
          }
Packit 5b56b6
Packit 5b56b6
        fp = fdopen (fd, mode);
Packit 5b56b6
        if (fp == NULL)
Packit 5b56b6
          {
Packit 5b56b6
            int saved_errno = errno;
Packit 5b56b6
            close (fd);
Packit 5b56b6
            errno = saved_errno;
Packit 5b56b6
          }
Packit 5b56b6
        return fp;
Packit 5b56b6
      }
Packit 5b56b6
  }
Packit 5b56b6
# endif
Packit 5b56b6
Packit 5b56b6
  return orig_fopen (filename, mode);
Packit 5b56b6
}