1127412Sgad/* Open a stream to a file. 2127412Sgad Copyright (C) 2007-2010 Free Software Foundation, Inc. 3127412Sgad 4127412Sgad This program is free software: you can redistribute it and/or modify 5127412Sgad it under the terms of the GNU General Public License as published by 6127412Sgad the Free Software Foundation; either version 3 of the License, or 7127431Sgad (at your option) any later version. 8127412Sgad 9127412Sgad This program is distributed in the hope that it will be useful, 10127412Sgad but WITHOUT ANY WARRANTY; without even the implied warranty of 11127412Sgad MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12127425Sgad GNU General Public License for more details. 13127425Sgad 14127425Sgad You should have received a copy of the GNU General Public License 15127412Sgad along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16127412Sgad 17127412Sgad/* Written by Bruno Haible <bruno@clisp.org>, 2007. */ 18127412Sgad 19#include <config.h> 20 21/* Get the original definition of fopen. It might be defined as a macro. */ 22#define __need_FILE 23#include <stdio.h> 24#undef __need_FILE 25 26static inline FILE * 27orig_fopen (const char *filename, const char *mode) 28{ 29 return fopen (filename, mode); 30} 31 32/* Specification. */ 33#include <stdio.h> 34 35#include <errno.h> 36#include <fcntl.h> 37#include <string.h> 38#include <unistd.h> 39#include <sys/types.h> 40#include <sys/stat.h> 41 42FILE * 43rpl_fopen (const char *filename, const char *mode) 44{ 45#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ 46 if (strcmp (filename, "/dev/null") == 0) 47 filename = "NUL"; 48#endif 49 50#if FOPEN_TRAILING_SLASH_BUG 51 /* If the filename ends in a slash and a mode that requires write access is 52 specified, then fail. 53 Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html> 54 says that 55 "A pathname that contains at least one non-slash character and that 56 ends with one or more trailing slashes shall be resolved as if a 57 single dot character ( '.' ) were appended to the pathname." 58 and 59 "The special filename dot shall refer to the directory specified by 60 its predecessor." 61 If the named file already exists as a directory, then if a mode that 62 requires write access is specified, fopen() must fail because POSIX 63 <http://www.opengroup.org/susv3/functions/fopen.html> says that it 64 fails with errno = EISDIR in this case. 65 If the named file does not exist or does not name a directory, then 66 fopen() must fail since the file does not contain a '.' directory. */ 67 { 68 size_t len = strlen (filename); 69 if (len > 0 && filename[len - 1] == '/') 70 { 71 int fd; 72 struct stat statbuf; 73 FILE *fp; 74 75 if (mode[0] == 'w' || mode[0] == 'a') 76 { 77 errno = EISDIR; 78 return NULL; 79 } 80 81 fd = open (filename, O_RDONLY); 82 if (fd < 0) 83 return NULL; 84 85 if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode)) 86 { 87 close (fd); 88 errno = ENOTDIR; 89 return NULL; 90 } 91 92 fp = fdopen (fd, mode); 93 if (fp == NULL) 94 { 95 int saved_errno = errno; 96 close (fd); 97 errno = saved_errno; 98 } 99 return fp; 100 } 101 } 102# endif 103 104 return orig_fopen (filename, mode); 105} 106