diff libinterp/corefcn/oct-stream.cc @ 31500:6bd338605fd3

Overhaul scanf functionality for correctness and Matlab compatibility (bug #63383, bug #62723) Return correct third output (error message) and fourth output (position) in many more cases. Functionality is still not correct in all situations and new corner cases were discovered while coding. Add BIST tests for correct behavior, some of which fail still. * oct-stream.cc (BEGIN_C_CONVERSION, BEGIN_S_CONVERSION): After reading characters, clear the failbit if the last character that was attempted to be read was EOF. This is not a true failure. * oct-stream.cc (BEGIN_CHAR_CLASS_CONVERSION): Move initialization of "width" variable to be with its definition as int. When reading characters and the read character is not in the character class, call putback(c) to place the character back into the input stream (cause of misplaced position output). After reading characters, clear the failbit if the last character that was attempted to be read was EOF. This is not a true failure. * oct-stream.cc (FINISH_CHARACTER_CONVERSION): Check for "width > 0", i.e., there were characters read, before updating outputs like conversion_count and position. * oct-stream.cc (base_stream::do_scanf): Remove test for "! eof" and just check for failbit. If failbit is set then issue a warning about pattern failing to match. * lo-utils.cc (read_fp_value): If reading has failed (failbit set) then clear eofbit which may have been set during the process. The calling routine will use seek() to restore file position to point before attempted read so EOF should no longer be set on stream. * io.tst: Add many more BIST cases.
author Rik <rik@octave.org>
date Mon, 21 Nov 2022 13:11:26 -0800
parents c90718a28a3c
children 2d38bc5a35db
line wrap: on
line diff
--- a/libinterp/corefcn/oct-stream.cc	Mon Nov 21 18:21:52 2022 +0100
+++ b/libinterp/corefcn/oct-stream.cc	Mon Nov 21 13:11:26 2022 -0800
@@ -4560,8 +4560,8 @@
          && (c = is.get ()) != std::istream::traits_type::eof ())       \
     tmp[n++] = static_cast<char> (c);                                   \
                                                                         \
-  if (n > 0 && c == std::istream::traits_type::eof ())                  \
-    is.clear ();                                                        \
+  if (c == std::istream::traits_type::eof ())                           \
+    is.clear (is.rdstate () & (~std::ios::failbit));                    \
                                                                         \
   tmp.resize (n)
 
@@ -4603,8 +4603,8 @@
                 tmp[n++] = static_cast<char> (c);                       \
             }                                                           \
                                                                         \
-          if (n > 0 && c == std::istream::traits_type::eof ())          \
-            is.clear ();                                                \
+          if (c == std::istream::traits_type::eof ())                   \
+            is.clear (is.rdstate () & (~std::ios::failbit));            \
                                                                         \
           tmp.resize (n);                                               \
         }                                                               \
@@ -4617,15 +4617,13 @@
 
   // This format must match a nonempty sequence of characters.
 #define BEGIN_CHAR_CLASS_CONVERSION()                                   \
-  int width = elt->width;                                               \
+  int width = (elt->width ? elt->width                                  \
+                          : std::numeric_limits<int>::max ());          \
                                                                         \
   std::string tmp;                                                      \
                                                                         \
   do                                                                    \
     {                                                                   \
-      if (! width)                                                      \
-        width = std::numeric_limits<int>::max ();                       \
-                                                                        \
       std::ostringstream buf;                                           \
                                                                         \
       std::string char_class = elt->char_class;                         \
@@ -4636,29 +4634,39 @@
         {                                                               \
           int chars_read = 0;                                           \
           while (is && chars_read++ < width                             \
-                 && (c = is.get ()) != std::istream::traits_type::eof () \
-                 && char_class.find (c) != std::string::npos)           \
-            buf << static_cast<char> (c);                               \
+                 && (c = is.get ()) != std::istream::traits_type::eof ()) \
+            {                                                           \
+              if (char_class.find (c) != std::string::npos)             \
+                buf << static_cast<char> (c);                           \
+              else                                                      \
+                {                                                       \
+                  is.putback (c);                                       \
+                  break;                                                \
+                }                                                       \
+            }                                                           \
         }                                                               \
       else                                                              \
         {                                                               \
           int chars_read = 0;                                           \
           while (is && chars_read++ < width                             \
-                 && (c = is.get ()) != std::istream::traits_type::eof () \
-                 && char_class.find (c) == std::string::npos)           \
-            buf << static_cast<char> (c);                               \
+                 && (c = is.get ()) != std::istream::traits_type::eof ()) \
+            {                                                           \
+              if (char_class.find (c) == std::string::npos)             \
+                buf << static_cast<char> (c);                           \
+              else                                                      \
+                {                                                       \
+                  is.putback (c);                                       \
+                  break;                                                \
+                }                                                       \
+            }                                                           \
         }                                                               \
                                                                         \
-      if (width == std::numeric_limits<int>::max ()                     \
-          && c != std::istream::traits_type::eof ())                    \
-        is.putback (c);                                                 \
-                                                                        \
       tmp = buf.str ();                                                 \
                                                                         \
       if (tmp.empty ())                                                 \
         is.setstate (std::ios::failbit);                                \
       else if (c == std::istream::traits_type::eof ())                  \
-        is.clear ();                                                    \
+        is.clear (is.rdstate () & (~std::ios::failbit));                \
                                                                         \
     }                                                                   \
   while (0)
@@ -4670,7 +4678,7 @@
         tmp = string::u8_from_encoding (who, tmp, encoding ());         \
       width = tmp.length ();                                            \
                                                                         \
-      if (is)                                                           \
+      if (is && width > 0)                                              \
         {                                                               \
           int i = 0;                                                    \
                                                                         \
@@ -5042,7 +5050,7 @@
 
                     // If it looks like we have a matching failure, then
                     // reset the failbit in the stream state.
-                    if (! is.eof () && is.rdstate () & std::ios::failbit)
+                    if (is.rdstate () & std::ios::failbit)
                       {
                         error (who, "format failed to match");
                         is.clear (is.rdstate () & (~std::ios::failbit));