Blame src/gl/fopen.c

Packit Service 991b93
/* Open a stream to a file.
Packit Service 991b93
   Copyright (C) 2007-2020 Free Software Foundation, Inc.
Packit Service 991b93
Packit Service 991b93
   This program is free software: you can redistribute it and/or modify
Packit Service 991b93
   it under the terms of the GNU General Public License as published by
Packit Service 991b93
   the Free Software Foundation; either version 3 of the License, or
Packit Service 991b93
   (at your option) any later version.
Packit Service 991b93
Packit Service 991b93
   This program is distributed in the hope that it will be useful,
Packit Service 991b93
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 991b93
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 991b93
   GNU General Public License for more details.
Packit Service 991b93
Packit Service 991b93
   You should have received a copy of the GNU General Public License
Packit Service 991b93
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit Service 991b93
Packit Service 991b93
/* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
Packit Service 991b93
Packit Service 991b93
/* If the user's config.h happens to include <stdio.h>, let it include only
Packit Service 991b93
   the system's <stdio.h> here, so that orig_fopen doesn't recurse to
Packit Service 991b93
   rpl_fopen.  */
Packit Service 991b93
#define __need_FILE
Packit Service 991b93
#include <config.h>
Packit Service 991b93
Packit Service 991b93
/* Get the original definition of fopen.  It might be defined as a macro.  */
Packit Service 991b93
#include <stdio.h>
Packit Service 991b93
#undef __need_FILE
Packit Service 991b93
Packit Service 991b93
static FILE *
Packit Service 991b93
orig_fopen (const char *filename, const char *mode)
Packit Service 991b93
{
Packit Service 991b93
  return fopen (filename, mode);
Packit Service 991b93
}
Packit Service 991b93
Packit Service 991b93
/* Specification.  */
Packit Service 991b93
/* Write "stdio.h" here, not <stdio.h>, otherwise OSF/1 5.1 DTK cc eliminates
Packit Service 991b93
   this include because of the preliminary #include <stdio.h> above.  */
Packit Service 991b93
#include "stdio.h"
Packit Service 991b93
Packit Service 991b93
#include <errno.h>
Packit Service 991b93
#include <fcntl.h>
Packit Service 991b93
#include <string.h>
Packit Service 991b93
#include <unistd.h>
Packit Service 991b93
#include <sys/types.h>
Packit Service 991b93
#include <sys/stat.h>
Packit Service 991b93
Packit Service 991b93
FILE *
Packit Service 991b93
rpl_fopen (const char *filename, const char *mode)
Packit Service 991b93
{
Packit Service 991b93
  int open_direction;
Packit Service 991b93
  int open_flags_standard;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
  int open_flags_gnu;
Packit Service 991b93
# define BUF_SIZE 80
Packit Service 991b93
  char fdopen_mode_buf[BUF_SIZE + 1];
Packit Service 991b93
#endif
Packit Service 991b93
  int open_flags;
Packit Service 991b93
Packit Service 991b93
#if defined _WIN32 && ! defined __CYGWIN__
Packit Service 991b93
  if (strcmp (filename, "/dev/null") == 0)
Packit Service 991b93
    filename = "NUL";
Packit Service 991b93
#endif
Packit Service 991b93
Packit Service 991b93
  /* Parse the mode.  */
Packit Service 991b93
  open_direction = 0;
Packit Service 991b93
  open_flags_standard = 0;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
  open_flags_gnu = 0;
Packit Service 991b93
#endif
Packit Service 991b93
  {
Packit Service 991b93
    const char *p = mode;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
    char *q = fdopen_mode_buf;
Packit Service 991b93
#endif
Packit Service 991b93
Packit Service 991b93
    for (; *p != '\0'; p++)
Packit Service 991b93
      {
Packit Service 991b93
        switch (*p)
Packit Service 991b93
          {
Packit Service 991b93
          case 'r':
Packit Service 991b93
            open_direction = O_RDONLY;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
            if (q < fdopen_mode_buf + BUF_SIZE)
Packit Service 991b93
              *q++ = *p;
Packit Service 991b93
#endif
Packit Service 991b93
            continue;
Packit Service 991b93
          case 'w':
Packit Service 991b93
            open_direction = O_WRONLY;
Packit Service 991b93
            open_flags_standard |= O_CREAT | O_TRUNC;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
            if (q < fdopen_mode_buf + BUF_SIZE)
Packit Service 991b93
              *q++ = *p;
Packit Service 991b93
#endif
Packit Service 991b93
            continue;
Packit Service 991b93
          case 'a':
Packit Service 991b93
            open_direction = O_WRONLY;
Packit Service 991b93
            open_flags_standard |= O_CREAT | O_APPEND;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
            if (q < fdopen_mode_buf + BUF_SIZE)
Packit Service 991b93
              *q++ = *p;
Packit Service 991b93
#endif
Packit Service 991b93
            continue;
Packit Service 991b93
          case 'b':
Packit Service 991b93
            /* While it is non-standard, O_BINARY is guaranteed by
Packit Service 991b93
               gnulib <fcntl.h>.  We can also assume that orig_fopen
Packit Service 991b93
               supports the 'b' flag.  */
Packit Service 991b93
            open_flags_standard |= O_BINARY;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
            if (q < fdopen_mode_buf + BUF_SIZE)
Packit Service 991b93
              *q++ = *p;
Packit Service 991b93
#endif
Packit Service 991b93
            continue;
Packit Service 991b93
          case '+':
Packit Service 991b93
            open_direction = O_RDWR;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
            if (q < fdopen_mode_buf + BUF_SIZE)
Packit Service 991b93
              *q++ = *p;
Packit Service 991b93
#endif
Packit Service 991b93
            continue;
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
          case 'x':
Packit Service 991b93
            open_flags_gnu |= O_EXCL;
Packit Service 991b93
            continue;
Packit Service 991b93
          case 'e':
Packit Service 991b93
            open_flags_gnu |= O_CLOEXEC;
Packit Service 991b93
            continue;
Packit Service 991b93
#endif
Packit Service 991b93
          default:
Packit Service 991b93
            break;
Packit Service 991b93
          }
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
        /* The rest of the mode string can be a platform-dependent extension.
Packit Service 991b93
           Copy it unmodified.  */
Packit Service 991b93
        {
Packit Service 991b93
          size_t len = strlen (p);
Packit Service 991b93
          if (len > fdopen_mode_buf + BUF_SIZE - q)
Packit Service 991b93
            len = fdopen_mode_buf + BUF_SIZE - q;
Packit Service 991b93
          memcpy (q, p, len);
Packit Service 991b93
          q += len;
Packit Service 991b93
        }
Packit Service 991b93
#endif
Packit Service 991b93
        break;
Packit Service 991b93
      }
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
    *q = '\0';
Packit Service 991b93
#endif
Packit Service 991b93
  }
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
  open_flags = open_flags_standard | open_flags_gnu;
Packit Service 991b93
#else
Packit Service 991b93
  open_flags = open_flags_standard;
Packit Service 991b93
#endif
Packit Service 991b93
Packit Service 991b93
#if FOPEN_TRAILING_SLASH_BUG
Packit Service 991b93
  /* Fail if the mode requires write access and the filename ends in a slash,
Packit Service 991b93
     as POSIX says such a filename must name a directory
Packit Service 991b93
     <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>:
Packit Service 991b93
       "A pathname that contains at least one non-<slash> character and that
Packit Service 991b93
        ends with one or more trailing <slash> characters shall not be resolved
Packit Service 991b93
        successfully unless the last pathname component before the trailing
Packit Service 991b93
        <slash> characters names an existing directory"
Packit Service 991b93
     If the named file already exists as a directory, then if a mode that
Packit Service 991b93
     requires write access is specified, fopen() must fail because POSIX
Packit Service 991b93
     <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html>
Packit Service 991b93
     says that it fails with errno = EISDIR in this case.
Packit Service 991b93
     If the named file does not exist or does not name a directory, then
Packit Service 991b93
     fopen() must fail since the file does not contain a '.' directory.  */
Packit Service 991b93
  {
Packit Service 991b93
    size_t len = strlen (filename);
Packit Service 991b93
    if (len > 0 && filename[len - 1] == '/')
Packit Service 991b93
      {
Packit Service 991b93
        int fd;
Packit Service 991b93
        struct stat statbuf;
Packit Service 991b93
        FILE *fp;
Packit Service 991b93
Packit Service 991b93
        if (open_direction != O_RDONLY)
Packit Service 991b93
          {
Packit Service 991b93
            errno = EISDIR;
Packit Service 991b93
            return NULL;
Packit Service 991b93
          }
Packit Service 991b93
Packit Service 991b93
        fd = open (filename, open_direction | open_flags);
Packit Service 991b93
        if (fd < 0)
Packit Service 991b93
          return NULL;
Packit Service 991b93
Packit Service 991b93
        if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
Packit Service 991b93
          {
Packit Service 991b93
            close (fd);
Packit Service 991b93
            errno = ENOTDIR;
Packit Service 991b93
            return NULL;
Packit Service 991b93
          }
Packit Service 991b93
Packit Service 991b93
# if GNULIB_FOPEN_GNU
Packit Service 991b93
        fp = fdopen (fd, fdopen_mode_buf);
Packit Service 991b93
# else
Packit Service 991b93
        fp = fdopen (fd, mode);
Packit Service 991b93
# endif
Packit Service 991b93
        if (fp == NULL)
Packit Service 991b93
          {
Packit Service 991b93
            int saved_errno = errno;
Packit Service 991b93
            close (fd);
Packit Service 991b93
            errno = saved_errno;
Packit Service 991b93
          }
Packit Service 991b93
        return fp;
Packit Service 991b93
      }
Packit Service 991b93
  }
Packit Service 991b93
#endif
Packit Service 991b93
Packit Service 991b93
#if GNULIB_FOPEN_GNU
Packit Service 991b93
  if (open_flags_gnu != 0)
Packit Service 991b93
    {
Packit Service 991b93
      int fd;
Packit Service 991b93
      FILE *fp;
Packit Service 991b93
Packit Service 991b93
      fd = open (filename, open_direction | open_flags);
Packit Service 991b93
      if (fd < 0)
Packit Service 991b93
        return NULL;
Packit Service 991b93
Packit Service 991b93
      fp = fdopen (fd, fdopen_mode_buf);
Packit Service 991b93
      if (fp == NULL)
Packit Service 991b93
        {
Packit Service 991b93
          int saved_errno = errno;
Packit Service 991b93
          close (fd);
Packit Service 991b93
          errno = saved_errno;
Packit Service 991b93
        }
Packit Service 991b93
      return fp;
Packit Service 991b93
    }
Packit Service 991b93
#endif
Packit Service 991b93
Packit Service 991b93
  return orig_fopen (filename, mode);
Packit Service 991b93
}