]> Savannah Git Hosting - gnulib.git/commitdiff
fopen: Fix the trailing slash workaround.
authorBruno Haible <bruno@clisp.org>
Sun, 24 May 2020 11:57:59 +0000 (13:57 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 24 May 2020 11:57:59 +0000 (13:57 +0200)
* 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.

ChangeLog
lib/fopen.c
tests/test-fopen.h

index c05ef910dc9ced74790fad32158c41a8d2a4a8a2..4dd90f83a138420dfcd72326b305ea18f7e32ec7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+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’
index a3128fa8b7ab159f05a86bd2a50b1cc6c1d80fde..ad6511d2c7d4eeb780e978b76d0db562b523b159 100644 (file)
@@ -47,11 +47,47 @@ orig_fopen (const char *filename, const char *mode)
 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
@@ -74,13 +110,13 @@ rpl_fopen (const char *filename, const char *mode)
         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;
 
@@ -101,7 +137,7 @@ rpl_fopen (const char *filename, const char *mode)
         return fp;
       }
   }
-# endif
+#endif
 
   return orig_fopen (filename, mode);
 }
index fb036af6b72e15399a20236d7dc5951999701010..875cdaf9818d87876c28a73d0faad0e9b6698154 100644 (file)
@@ -47,6 +47,10 @@ test_fopen (void)
   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);
@@ -58,6 +62,18 @@ test_fopen (void)
   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);