Home | History | Annotate | Download | only in lib
      1 /* Open a stream to a file.
      2    Copyright (C) 2007-2012 Free Software Foundation, Inc.
      3 
      4    This program is free software: you can redistribute it and/or modify
      5    it under the terms of the GNU General Public License as published by
      6    the Free Software Foundation; either version 3 of the License, or
      7    (at your option) any later version.
      8 
      9    This program 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 General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License
     15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     16 
     17 /* Written by Bruno Haible <bruno (at) 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 __need_FILE
     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 __need_FILE
     28 
     29 static FILE *
     30 orig_fopen (const char *filename, const char *mode)
     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 <string.h>
     43 #include <unistd.h>
     44 #include <sys/types.h>
     45 #include <sys/stat.h>
     46 
     47 FILE *
     48 rpl_fopen (const char *filename, const char *mode)
     49 {
     50 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
     51   if (strcmp (filename, "/dev/null") == 0)
     52     filename = "NUL";
     53 #endif
     54 
     55 #if FOPEN_TRAILING_SLASH_BUG
     56   /* If the filename ends in a slash and a mode that requires write access is
     57      specified, then fail.
     58      Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
     59      says that
     60        "A pathname that contains at least one non-slash character and that
     61         ends with one or more trailing slashes shall be resolved as if a
     62         single dot character ( '.' ) were appended to the pathname."
     63      and
     64        "The special filename dot shall refer to the directory specified by
     65         its predecessor."
     66      If the named file already exists as a directory, then if a mode that
     67      requires write access is specified, fopen() must fail because POSIX
     68      <http://www.opengroup.org/susv3/functions/fopen.html> says that it
     69      fails with errno = EISDIR in this case.
     70      If the named file does not exist or does not name a directory, then
     71      fopen() must fail since the file does not contain a '.' directory.  */
     72   {
     73     size_t len = strlen (filename);
     74     if (len > 0 && filename[len - 1] == '/')
     75       {
     76         int fd;
     77         struct stat statbuf;
     78         FILE *fp;
     79 
     80         if (mode[0] == 'w' || mode[0] == 'a')
     81           {
     82             errno = EISDIR;
     83             return NULL;
     84           }
     85 
     86         fd = open (filename, O_RDONLY);
     87         if (fd < 0)
     88           return NULL;
     89 
     90         if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
     91           {
     92             close (fd);
     93             errno = ENOTDIR;
     94             return NULL;
     95           }
     96 
     97         fp = fdopen (fd, mode);
     98         if (fp == NULL)
     99           {
    100             int saved_errno = errno;
    101             close (fd);
    102             errno = saved_errno;
    103           }
    104         return fp;
    105       }
    106   }
    107 # endif
    108 
    109   return orig_fopen (filename, mode);
    110 }
    111