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