root/maint/gnulib/lib/fopen.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. orig_fopen
  2. rpl_fopen

   1 /* Open a stream to a file.
   2    Copyright (C) 2007-2021 Free Software Foundation, Inc.
   3 
   4    This file is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU Lesser General Public License as
   6    published by the Free Software Foundation; either version 2.1 of the
   7    License, or (at your option) any later version.
   8 
   9    This file is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU Lesser General Public License for more details.
  13 
  14    You should have received a copy of the GNU Lesser General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
  18 
  19 /* If the user's config.h happens to include <stdio.h>, let it include only
  20    the system's <stdio.h> here, so that orig_fopen doesn't recurse to
  21    rpl_fopen.  */
  22 #define _GL_ALREADY_INCLUDING_STDIO_H
  23 #include <config.h>
  24 
  25 /* Get the original definition of fopen.  It might be defined as a macro.  */
  26 #include <stdio.h>
  27 #undef _GL_ALREADY_INCLUDING_STDIO_H
  28 
  29 static FILE *
  30 orig_fopen (const char *filename, const char *mode)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32   return fopen (filename, mode);
  33 }
  34 
  35 /* Specification.  */
  36 /* Write "stdio.h" here, not <stdio.h>, otherwise OSF/1 5.1 DTK cc eliminates
  37    this include because of the preliminary #include <stdio.h> above.  */
  38 #include "stdio.h"
  39 
  40 #include <errno.h>
  41 #include <fcntl.h>
  42 #include <stdbool.h>
  43 #include <string.h>
  44 #include <unistd.h>
  45 #include <sys/types.h>
  46 #include <sys/stat.h>
  47 
  48 FILE *
  49 rpl_fopen (const char *filename, const char *mode)
     /* [previous][next][first][last][top][bottom][index][help] */
  50 {
  51   int open_direction;
  52   int open_flags;
  53 #if GNULIB_FOPEN_GNU
  54   bool open_flags_gnu;
  55 # define BUF_SIZE 80
  56   char fdopen_mode_buf[BUF_SIZE + 1];
  57 #endif
  58 
  59 #if defined _WIN32 && ! defined __CYGWIN__
  60   if (strcmp (filename, "/dev/null") == 0)
  61     filename = "NUL";
  62 #endif
  63 
  64   /* Parse the mode.  */
  65   open_direction = 0;
  66   open_flags = 0;
  67 #if GNULIB_FOPEN_GNU
  68   open_flags_gnu = false;
  69 #endif
  70   {
  71     const char *p = mode;
  72 #if GNULIB_FOPEN_GNU
  73     char *q = fdopen_mode_buf;
  74 #endif
  75 
  76     for (; *p != '\0'; p++)
  77       {
  78         switch (*p)
  79           {
  80           case 'r':
  81             open_direction = O_RDONLY;
  82 #if GNULIB_FOPEN_GNU
  83             if (q < fdopen_mode_buf + BUF_SIZE)
  84               *q++ = *p;
  85 #endif
  86             continue;
  87           case 'w':
  88             open_direction = O_WRONLY;
  89             open_flags |= O_CREAT | O_TRUNC;
  90 #if GNULIB_FOPEN_GNU
  91             if (q < fdopen_mode_buf + BUF_SIZE)
  92               *q++ = *p;
  93 #endif
  94             continue;
  95           case 'a':
  96             open_direction = O_WRONLY;
  97             open_flags |= O_CREAT | O_APPEND;
  98 #if GNULIB_FOPEN_GNU
  99             if (q < fdopen_mode_buf + BUF_SIZE)
 100               *q++ = *p;
 101 #endif
 102             continue;
 103           case 'b':
 104             /* While it is non-standard, O_BINARY is guaranteed by
 105                gnulib <fcntl.h>.  We can also assume that orig_fopen
 106                supports the 'b' flag.  */
 107             open_flags |= O_BINARY;
 108 #if GNULIB_FOPEN_GNU
 109             if (q < fdopen_mode_buf + BUF_SIZE)
 110               *q++ = *p;
 111 #endif
 112             continue;
 113           case '+':
 114             open_direction = O_RDWR;
 115 #if GNULIB_FOPEN_GNU
 116             if (q < fdopen_mode_buf + BUF_SIZE)
 117               *q++ = *p;
 118 #endif
 119             continue;
 120 #if GNULIB_FOPEN_GNU
 121           case 'x':
 122             open_flags |= O_EXCL;
 123             open_flags_gnu = true;
 124             continue;
 125           case 'e':
 126             open_flags |= O_CLOEXEC;
 127             open_flags_gnu = true;
 128             continue;
 129 #endif
 130           default:
 131             break;
 132           }
 133 #if GNULIB_FOPEN_GNU
 134         /* The rest of the mode string can be a platform-dependent extension.
 135            Copy it unmodified.  */
 136         {
 137           size_t len = strlen (p);
 138           if (len > fdopen_mode_buf + BUF_SIZE - q)
 139             len = fdopen_mode_buf + BUF_SIZE - q;
 140           memcpy (q, p, len);
 141           q += len;
 142         }
 143 #endif
 144         break;
 145       }
 146 #if GNULIB_FOPEN_GNU
 147     *q = '\0';
 148 #endif
 149   }
 150 
 151 #if FOPEN_TRAILING_SLASH_BUG
 152   /* Fail if the mode requires write access and the filename ends in a slash,
 153      as POSIX says such a filename must name a directory
 154      <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>:
 155        "A pathname that contains at least one non-<slash> character and that
 156         ends with one or more trailing <slash> characters shall not be resolved
 157         successfully unless the last pathname component before the trailing
 158         <slash> characters names an existing directory"
 159      If the named file already exists as a directory, then if a mode that
 160      requires write access is specified, fopen() must fail because POSIX
 161      <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html>
 162      says that it fails with errno = EISDIR in this case.
 163      If the named file does not exist or does not name a directory, then
 164      fopen() must fail since the file does not contain a '.' directory.  */
 165   {
 166     size_t len = strlen (filename);
 167     if (len > 0 && filename[len - 1] == '/')
 168       {
 169         int fd;
 170         struct stat statbuf;
 171         FILE *fp;
 172 
 173         if (open_direction != O_RDONLY)
 174           {
 175             errno = EISDIR;
 176             return NULL;
 177           }
 178 
 179         fd = open (filename, open_direction | open_flags,
 180                    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
 181         if (fd < 0)
 182           return NULL;
 183 
 184         if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
 185           {
 186             close (fd);
 187             errno = ENOTDIR;
 188             return NULL;
 189           }
 190 
 191 # if GNULIB_FOPEN_GNU
 192         fp = fdopen (fd, fdopen_mode_buf);
 193 # else
 194         fp = fdopen (fd, mode);
 195 # endif
 196         if (fp == NULL)
 197           {
 198             int saved_errno = errno;
 199             close (fd);
 200             errno = saved_errno;
 201           }
 202         return fp;
 203       }
 204   }
 205 #endif
 206 
 207 #if GNULIB_FOPEN_GNU
 208   if (open_flags_gnu)
 209     {
 210       int fd;
 211       FILE *fp;
 212 
 213       fd = open (filename, open_direction | open_flags,
 214                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
 215       if (fd < 0)
 216         return NULL;
 217 
 218       fp = fdopen (fd, fdopen_mode_buf);
 219       if (fp == NULL)
 220         {
 221           int saved_errno = errno;
 222           close (fd);
 223           errno = saved_errno;
 224         }
 225       return fp;
 226     }
 227 #endif
 228 
 229   return orig_fopen (filename, mode);
 230 }

/* [previous][next][first][last][top][bottom][index][help] */