summaryrefslogtreecommitdiffstats
path: root/gl/fopen.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/fopen.c')
-rw-r--r--gl/fopen.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/gl/fopen.c b/gl/fopen.c
new file mode 100644
index 00000000..f8469a0b
--- /dev/null
+++ b/gl/fopen.c
@@ -0,0 +1,229 @@
1/* Open a stream to a file.
2 Copyright (C) 2007-2023 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
29static FILE *
30orig_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
47FILE *
48rpl_fopen (const char *filename, const char *mode)
49{
50 int open_direction;
51 int open_flags;
52#if GNULIB_FOPEN_GNU
53 bool open_flags_gnu;
54# define BUF_SIZE 80
55 char fdopen_mode_buf[BUF_SIZE + 1];
56#endif
57
58#if defined _WIN32 && ! defined __CYGWIN__
59 if (strcmp (filename, "/dev/null") == 0)
60 filename = "NUL";
61#endif
62
63 /* Parse the mode. */
64 open_direction = 0;
65 open_flags = 0;
66#if GNULIB_FOPEN_GNU
67 open_flags_gnu = false;
68#endif
69 {
70 const char *p = mode;
71#if GNULIB_FOPEN_GNU
72 char *q = fdopen_mode_buf;
73#endif
74
75 for (; *p != '\0'; p++)
76 {
77 switch (*p)
78 {
79 case 'r':
80 open_direction = O_RDONLY;
81#if GNULIB_FOPEN_GNU
82 if (q < fdopen_mode_buf + BUF_SIZE)
83 *q++ = *p;
84#endif
85 continue;
86 case 'w':
87 open_direction = O_WRONLY;
88 open_flags |= O_CREAT | O_TRUNC;
89#if GNULIB_FOPEN_GNU
90 if (q < fdopen_mode_buf + BUF_SIZE)
91 *q++ = *p;
92#endif
93 continue;
94 case 'a':
95 open_direction = O_WRONLY;
96 open_flags |= O_CREAT | O_APPEND;
97#if GNULIB_FOPEN_GNU
98 if (q < fdopen_mode_buf + BUF_SIZE)
99 *q++ = *p;
100#endif
101 continue;
102 case 'b':
103 /* While it is non-standard, O_BINARY is guaranteed by
104 gnulib <fcntl.h>. We can also assume that orig_fopen
105 supports the 'b' flag. */
106 open_flags |= O_BINARY;
107#if GNULIB_FOPEN_GNU
108 if (q < fdopen_mode_buf + BUF_SIZE)
109 *q++ = *p;
110#endif
111 continue;
112 case '+':
113 open_direction = O_RDWR;
114#if GNULIB_FOPEN_GNU
115 if (q < fdopen_mode_buf + BUF_SIZE)
116 *q++ = *p;
117#endif
118 continue;
119#if GNULIB_FOPEN_GNU
120 case 'x':
121 open_flags |= O_EXCL;
122 open_flags_gnu = true;
123 continue;
124 case 'e':
125 open_flags |= O_CLOEXEC;
126 open_flags_gnu = true;
127 continue;
128#endif
129 default:
130 break;
131 }
132#if GNULIB_FOPEN_GNU
133 /* The rest of the mode string can be a platform-dependent extension.
134 Copy it unmodified. */
135 {
136 size_t len = strlen (p);
137 if (len > fdopen_mode_buf + BUF_SIZE - q)
138 len = fdopen_mode_buf + BUF_SIZE - q;
139 memcpy (q, p, len);
140 q += len;
141 }
142#endif
143 break;
144 }
145#if GNULIB_FOPEN_GNU
146 *q = '\0';
147#endif
148 }
149
150#if FOPEN_TRAILING_SLASH_BUG
151 /* Fail if the mode requires write access and the filename ends in a slash,
152 as POSIX says such a filename must name a directory
153 <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>:
154 "A pathname that contains at least one non-<slash> character and that
155 ends with one or more trailing <slash> characters shall not be resolved
156 successfully unless the last pathname component before the trailing
157 <slash> characters names an existing directory"
158 If the named file already exists as a directory, then if a mode that
159 requires write access is specified, fopen() must fail because POSIX
160 <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html>
161 says that it fails with errno = EISDIR in this case.
162 If the named file does not exist or does not name a directory, then
163 fopen() must fail since the file does not contain a '.' directory. */
164 {
165 size_t len = strlen (filename);
166 if (len > 0 && filename[len - 1] == '/')
167 {
168 int fd;
169 struct stat statbuf;
170 FILE *fp;
171
172 if (open_direction != O_RDONLY)
173 {
174 errno = EISDIR;
175 return NULL;
176 }
177
178 fd = open (filename, open_direction | open_flags,
179 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
180 if (fd < 0)
181 return NULL;
182
183 if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
184 {
185 close (fd);
186 errno = ENOTDIR;
187 return NULL;
188 }
189
190# if GNULIB_FOPEN_GNU
191 fp = fdopen (fd, fdopen_mode_buf);
192# else
193 fp = fdopen (fd, mode);
194# endif
195 if (fp == NULL)
196 {
197 int saved_errno = errno;
198 close (fd);
199 errno = saved_errno;
200 }
201 return fp;
202 }
203 }
204#endif
205
206#if GNULIB_FOPEN_GNU
207 if (open_flags_gnu)
208 {
209 int fd;
210 FILE *fp;
211
212 fd = open (filename, open_direction | open_flags,
213 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
214 if (fd < 0)
215 return NULL;
216
217 fp = fdopen (fd, fdopen_mode_buf);
218 if (fp == NULL)
219 {
220 int saved_errno = errno;
221 close (fd);
222 errno = saved_errno;
223 }
224 return fp;
225 }
226#endif
227
228 return orig_fopen (filename, mode);
229}