+2020-05-24 Bruno Haible <bruno@clisp.org>
+
+ fopen: Fix the trailing slash workaround.
+ * lib/fopen.c (rpl_fopen): Parse the mode string. Recognize "r+" as a
+ write access. Pass the right flags to open().
+ * tests/test-fopen.h (test_fopen): Add a few more tests on directories.
+
2020-05-23 Paul Eggert <eggert@cs.ucla.edu>
assure: new macro ‘affirm’
FILE *
rpl_fopen (const char *filename, const char *mode)
{
+ int open_direction;
+ int open_flags_standard;
+
#if defined _WIN32 && ! defined __CYGWIN__
if (strcmp (filename, "/dev/null") == 0)
filename = "NUL";
#endif
+ /* Parse the mode. */
+ open_direction = 0;
+ open_flags_standard = 0;
+ {
+ const char *m;
+
+ for (m = mode; *m != '\0'; m++)
+ {
+ switch (*m)
+ {
+ case 'r':
+ open_direction = O_RDONLY;
+ continue;
+ case 'w':
+ open_direction = O_WRONLY;
+ open_flags_standard |= O_CREAT | O_TRUNC;
+ continue;
+ case 'a':
+ open_direction = O_WRONLY;
+ open_flags_standard |= O_CREAT | O_APPEND;
+ continue;
+ case 'b':
+ continue;
+ case '+':
+ open_direction = O_RDWR;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
#if FOPEN_TRAILING_SLASH_BUG
/* Fail if the mode requires write access and the filename ends in a slash,
as POSIX says such a filename must name a directory
struct stat statbuf;
FILE *fp;
- if (mode[0] == 'w' || mode[0] == 'a')
+ if (open_direction != O_RDONLY)
{
errno = EISDIR;
return NULL;
}
- fd = open (filename, O_RDONLY);
+ fd = open (filename, open_direction | open_flags_standard);
if (fd < 0)
return NULL;
return fp;
}
}
-# endif
+#endif
return orig_fopen (filename, mode);
}
ASSERT (fopen (BASE "file/", "r") == NULL);
ASSERT (errno == ENOTDIR || errno == EISDIR || errno == EINVAL);
+ errno = 0;
+ ASSERT (fopen (BASE "file/", "r+") == NULL);
+ ASSERT (errno == ENOTDIR || errno == EISDIR || errno == EINVAL);
+
/* Cannot create a directory. */
errno = 0;
ASSERT (fopen ("nonexist.ent/", "w") == NULL);
ASSERT (fopen (".", "w") == NULL);
ASSERT (errno == EISDIR || errno == EINVAL || errno == EACCES);
+ errno = 0;
+ ASSERT (fopen ("./", "w") == NULL);
+ ASSERT (errno == EISDIR || errno == EINVAL || errno == EACCES);
+
+ errno = 0;
+ ASSERT (fopen (".", "r+") == NULL);
+ ASSERT (errno == EISDIR || errno == EINVAL || errno == EACCES);
+
+ errno = 0;
+ ASSERT (fopen ("./", "r+") == NULL);
+ ASSERT (errno == EISDIR || errno == EINVAL || errno == EACCES);
+
/* /dev/null must exist, and be writable. */
f = fopen ("/dev/null", "r");
ASSERT (f);