changeset 16590:2d968b7830d6

handle A, R, and W fopen modes correctly (bug #38851) * file-io.cc (normalize_fopen_mode): New function. Handle 'A'. Also handle 'b' and 't' suffixes here. Use Octave:fopen-mode warning id. (fopen_mode_to_ios_mode): Only convert from mode string to ios mode. (do_stream_open): Call normalize_fopen_mode before calling fopen_mode_to_ios_mode. Don't process mode string directly. * io.tst: Update test for fopen modes.
author John W. Eaton <jwe@octave.org>
date Mon, 29 Apr 2013 13:46:55 -0400
parents fe6beca15813
children 2931e9282190
files libinterp/interpfcn/file-io.cc test/io.tst
diffstat 2 files changed, 87 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/interpfcn/file-io.cc	Mon Apr 29 00:47:36 2013 -0400
+++ b/libinterp/interpfcn/file-io.cc	Mon Apr 29 13:46:55 2013 -0400
@@ -130,26 +130,25 @@
     }
 }
 
-static std::ios::openmode
-fopen_mode_to_ios_mode (const std::string& mode_arg)
+static void
+normalize_fopen_mode (std::string& mode, bool& use_zlib)
 {
-  std::ios::openmode retval = std::ios::in;
+  use_zlib = false;
 
-  if (! mode_arg.empty ())
+  if (! mode.empty ())
     {
       // Could probably be faster, but does it really matter?
 
-      std::string mode = mode_arg;
-
-      // 'W' and 'R' are accepted as 'w' and 'r', but we warn about
-      // them because Matlab says they perform "automatic flushing"
-      // but we don't know precisely what action that implies.
+      // Accept 'W', 'R', and 'A' as 'w', 'r', and 'a' but we warn about
+      // them because Matlab says they don't perform "automatic
+      // flushing" but we don't know precisely what action that implies.
 
       size_t pos = mode.find ('W');
 
       if (pos != std::string::npos)
         {
-          warning ("fopen: treating mode \"W\" as equivalent to \"w\"");
+          warning_with_id ("Octave:fopen-mode",
+                           "fopen: treating mode \"W\" as equivalent to \"w\"");
           mode[pos] = 'w';
         }
 
@@ -157,15 +156,26 @@
 
       if (pos != std::string::npos)
         {
-          warning ("fopen: treating mode \"R\" as equivalent to \"r\"");
+          warning_with_id ("Octave:fopen-mode",
+                           "fopen: treating mode \"R\" as equivalent to \"r\"");
           mode[pos] = 'r';
         }
 
+      pos = mode.find ('A');
+
+      if (pos != std::string::npos)
+        {
+          warning_with_id ("Octave:fopen-mode",
+                           "fopen: treating mode \"A\" as equivalent to \"a\"");
+          mode[pos] = 'a';
+        }
+
       pos = mode.find ('z');
 
       if (pos != std::string::npos)
         {
 #if defined (HAVE_ZLIB)
+          use_zlib = true;
           mode.erase (pos, 1);
 #else
           error ("this version of Octave does not support gzipped files");
@@ -174,36 +184,54 @@
 
       if (! error_state)
         {
-          if (mode == "rt")
-            retval = std::ios::in;
-          else if (mode == "wt")
-            retval = std::ios::out | std::ios::trunc;
-          else if (mode == "at")
-            retval = std::ios::out | std::ios::app;
-          else if (mode == "r+t" || mode == "rt+")
-            retval = std::ios::in | std::ios::out;
-          else if (mode == "w+t" || mode == "wt+")
-            retval = std::ios::in | std::ios::out | std::ios::trunc;
-          else if (mode == "a+t" || mode == "at+")
-            retval = std::ios::in | std::ios::out | std::ios::app;
-          else if (mode == "rb" || mode == "r")
-            retval = std::ios::in | std::ios::binary;
-          else if (mode == "wb" || mode == "w")
-            retval = std::ios::out | std::ios::trunc | std::ios::binary;
-          else if (mode == "ab" || mode == "a")
-            retval = std::ios::out | std::ios::app | std::ios::binary;
-          else if (mode == "r+b" || mode == "rb+" || mode == "r+")
-            retval = std::ios::in | std::ios::out | std::ios::binary;
-          else if (mode == "w+b" || mode == "wb+" || mode == "w+")
-            retval = (std::ios::in | std::ios::out | std::ios::trunc
-                      | std::ios::binary);
-          else if (mode == "a+b" || mode == "ab+" || mode == "a+")
-            retval = (std::ios::in | std::ios::out | std::ios::app
-                      | std::ios::binary);
-          else
-            ::error ("invalid mode specified");
+          // Use binary mode if 't' is not specified, but don't add
+          // 'b' if it is already present.
+
+          size_t bpos = mode.find ('b');
+          size_t tpos = mode.find ('t');
+
+          if (bpos == std::string::npos && tpos == std::string::npos)
+            mode += 'b';
         }
     }
+}
+
+static std::ios::openmode
+fopen_mode_to_ios_mode (const std::string& mode)
+{
+  std::ios::openmode retval = std::ios::in;
+
+  if (! error_state)
+    {
+      if (mode == "rt")
+        retval = std::ios::in;
+      else if (mode == "wt")
+        retval = std::ios::out | std::ios::trunc;
+      else if (mode == "at")
+        retval = std::ios::out | std::ios::app;
+      else if (mode == "r+t" || mode == "rt+")
+        retval = std::ios::in | std::ios::out;
+      else if (mode == "w+t" || mode == "wt+")
+        retval = std::ios::in | std::ios::out | std::ios::trunc;
+      else if (mode == "a+t" || mode == "at+")
+        retval = std::ios::in | std::ios::out | std::ios::app;
+      else if (mode == "rb" || mode == "r")
+        retval = std::ios::in | std::ios::binary;
+      else if (mode == "wb" || mode == "w")
+        retval = std::ios::out | std::ios::trunc | std::ios::binary;
+      else if (mode == "ab" || mode == "a")
+        retval = std::ios::out | std::ios::app | std::ios::binary;
+      else if (mode == "r+b" || mode == "rb+" || mode == "r+")
+        retval = std::ios::in | std::ios::out | std::ios::binary;
+      else if (mode == "w+b" || mode == "wb+" || mode == "w+")
+        retval = (std::ios::in | std::ios::out | std::ios::trunc
+                  | std::ios::binary);
+      else if (mode == "a+b" || mode == "ab+" || mode == "a+")
+        retval = (std::ios::in | std::ios::out | std::ios::app
+                  | std::ios::binary);
+      else
+        ::error ("invalid mode specified");
+    }
 
   return retval;
 }
@@ -448,13 +476,17 @@
 
 
 static octave_stream
-do_stream_open (const std::string& name, const std::string& mode,
+do_stream_open (const std::string& name, const std::string& mode_arg,
                 const std::string& arch, int& fid)
 {
   octave_stream retval;
 
   fid = -1;
 
+  std::string mode = mode_arg;
+  bool use_zlib = false;
+  normalize_fopen_mode (mode, use_zlib);
+
   std::ios::openmode md = fopen_mode_to_ios_mode (mode);
 
   if (! error_state)
@@ -488,29 +520,14 @@
 
           if (! fs.is_dir ())
             {
-              std::string tmode = mode;
-
-              // Use binary mode if 't' is not specified, but don't add
-              // 'b' if it is already present.
-
-              size_t bpos = tmode.find ('b');
-              size_t tpos = tmode.find ('t');
-
-              if (bpos == std::string::npos && tpos == std::string::npos)
-                tmode += 'b';
-
 #if defined (HAVE_ZLIB)
-              size_t pos = tmode.find ('z');
-
-              if (pos != std::string::npos)
+              if (use_zlib)
                 {
-                  tmode.erase (pos, 1);
-
-                  FILE *fptr = gnulib::fopen (fname.c_str (), tmode.c_str ());
+                  FILE *fptr = gnulib::fopen (fname.c_str (), mode.c_str ());
 
                   int fd = fileno (fptr);
 
-                  gzFile gzf = ::gzdopen (fd, tmode.c_str ());
+                  gzFile gzf = ::gzdopen (fd, mode.c_str ());
 
                   if (fptr)
                     retval = octave_zstdiostream::create (fname, gzf, fd,
@@ -521,7 +538,7 @@
               else
 #endif
                 {
-                  FILE *fptr = gnulib::fopen (fname.c_str (), tmode.c_str ());
+                  FILE *fptr = gnulib::fopen (fname.c_str (), mode.c_str ());
 
                   retval = octave_stdiostream::create (fname, fptr, md, flt_fmt);
 
--- a/test/io.tst	Mon Apr 29 00:47:36 2013 -0400
+++ b/test/io.tst	Mon Apr 29 13:46:55 2013 -0400
@@ -335,21 +335,25 @@
 
 %% test/octave.test/io/fopen-1.m
 %!test
-%! arch_list = ["native"; "ieee-le"; "ieee-be"; "vaxd"; "vaxg"; "cray"];
-%!
+%! arch_list = {"native"; "ieee-le"; "ieee-be"; "vaxd"; "vaxg"; "cray"};
+%! warning ("off", "Octave:fopen-mode")
 %! status = 1;
 %!
 %! for i = 1:6
-%!   arch = deblank (arch_list (i,:));
-%!   for j = 1:6
+%!   arch = arch_list{i};
+%!   for j = 1:4
 %!     if (j == 1)
-%!       mode_list = ["w"; "r"; "a"];
+%!       mode_list = {"w"; "r"; "a"};
 %!     elseif (j == 2)
-%!       mode_list = ["w+"; "r+"; "a+"];
+%!       mode_list = {"w+"; "r+"; "a+"};
+%!     elseif (j == 3)
+%!       mode_list = {"W"; "R"; "A"};
+%!     elseif (j == 4)
+%!       mode_list = {"W+"; "R+"; "A+"};
 %!     endif
 %!     nm = tmpnam ();
 %!     for k = 1:3
-%!       mode = deblank (mode_list (k,:));
+%!       mode = mode_list{k};
 %!       [id, err] = fopen (nm, mode, arch);
 %!       if (id < 0)
 %!         __printf_assert__ ("open failed: %s (%s, %s): %s\n", nm, mode, arch, err);