changeset 31504:d006285b7509

Fix scanf handling of bare '-' character for floating point formats (bug #63383) * oct-stream.cc (octave_scan): Use "is >> std::ws" instead of hand-rolled code to skipe whitespace. Capture stream pointer before calling read_value(). If read failed (failbit set) then restore stream pointer for correct positioning. * lo-utils.cc (read_fp_value): Use "is >> std::ws" instead of hand-rolled code to skipe whitespace. If character after '+' or '-' is whitespace, rather than a number, then set failbit on stream because this is a pattern match error. * io.tst: Fix BIST test copy&paste error for Inf-reading test.
author Rik <rik@octave.org>
date Tue, 22 Nov 2022 08:23:44 -0800
parents 2a12350f9410
children fb123529131b
files libinterp/corefcn/oct-stream.cc liboctave/util/lo-utils.cc test/io.tst
diffstat 3 files changed, 22 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/oct-stream.cc	Tue Nov 22 12:48:29 2022 +0100
+++ b/libinterp/corefcn/oct-stream.cc	Tue Nov 22 08:23:44 2022 -0800
@@ -4441,17 +4441,17 @@
       case 'E':
       case 'G':
         {
-          int c1 = std::istream::traits_type::eof ();
-
-          while (is && (c1 = is.get ()) != std::istream::traits_type::eof ()
-                 && isspace (c1))
-            ; // skip whitespace
-
-          if (c1 != std::istream::traits_type::eof ())
+          is >> std::ws;  // skip through whitespace and advance stream pointer
+          std::streampos pos = is.tellg ();
+
+          ref = read_value<double> (is);
+
+          std::ios::iostate status = is.rdstate ();
+          if (status & std::ios::failbit)
             {
-              is.putback (c1);
-
-              ref = read_value<double> (is);
+              is.clear ();
+              is.seekg (pos);
+              is.setstate (status & ~std::ios_base::eofbit);
             }
         }
         break;
--- a/liboctave/util/lo-utils.cc	Tue Nov 22 12:48:29 2022 +0100
+++ b/liboctave/util/lo-utils.cc	Tue Nov 22 08:23:44 2022 -0800
@@ -263,16 +263,13 @@
     T val = 0.0;
 
     // FIXME: resetting stream position is likely to fail unless we are
-    // reading from a file.
+    //        reading from a file.
     std::streampos pos = is.tellg ();
 
-    char c1 = ' ';
-
-    while (isspace (c1))
-      c1 = is.get ();
+    is >> std::ws;  // skip through whitespace and advance stream pointer
 
     bool neg = false;
-
+    char c1 = is.get ();
     switch (c1)
       {
       case '-':
@@ -285,6 +282,8 @@
           c2 = is.get ();
           if (c2 == 'i' || c2 == 'I' || c2 == 'n' || c2 == 'N')
             val = read_inf_nan_na<T> (is, c2);
+          else if (isspace (c2))
+            is.setstate (std::ios::failbit);
           else
             {
               is.putback (c2);
--- a/test/io.tst	Tue Nov 22 12:48:29 2022 +0100
+++ b/test/io.tst	Tue Nov 22 08:23:44 2022 -0800
@@ -556,13 +556,13 @@
 %! assert (count, 2);
 %! assert (msg, 'sscanf: format failed to match');
 %! assert (pos, 5);
-%! [val, count, msg, pos] = sscanf ('2 3 na', '%f');
-%! assert (val, [2; 3; NA]);
-%! assert (count, 3);
-%! assert (msg, '');
-%! assert (pos, 7);
-%! [val, count, msg, pos] = sscanf ('2 3 nan', '%f');
-%! assert (val, [2; 3; NaN]);
+%! [val, count, msg, pos] = sscanf ('2 3 in', '%f');
+%! assert (val, [2; 3]);
+%! assert (count, 2);
+%! assert (msg, 'sscanf: format failed to match');
+%! assert (pos, 5);
+%! [val, count, msg, pos] = sscanf ('2 3 inf', '%f');
+%! assert (val, [2; 3; Inf]);
 %! assert (count, 3);
 %! assert (msg, '');
 %! assert (pos, 8);