Mercurial > octave
changeset 31542:2f08a53e0a23
Fix scanf handling of exceptional values (NA, NaN, Inf) at EOF (bug #63383)
* oct-stream.cc (octave_scan): After skipping whitespace, check stream with
is.good() before proceeding to read a value.
* lo-utils.cc (read_inf_nan_na): Use is.peek() after successfully reading Inf
or NaN to possibly set EOF bit.
* lo-utils.cc (read_fp_value): Add extra test that value is not NA or NaN
before changing it to -value when negative '-' character present.
* io.tst: Mark fixed BIST tests with '*' regression marker.
Add BIST tests for exceptional '+' character and for +/-NA, +/-NaN.
author | Rik <rik@octave.org> |
---|---|
date | Fri, 25 Nov 2022 08:30:30 -0800 |
parents | ce5b4a00b022 |
children | 85a073f7c5f9 |
files | libinterp/corefcn/oct-stream.cc liboctave/util/lo-utils.cc test/io.tst |
diffstat | 3 files changed, 77 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/oct-stream.cc Thu Nov 24 16:16:52 2022 -0500 +++ b/libinterp/corefcn/oct-stream.cc Fri Nov 25 08:30:30 2022 -0800 @@ -4442,16 +4442,19 @@ case 'G': { 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) + if (is.good ()) { - is.clear (); - is.seekg (pos); - is.setstate (status & ~std::ios_base::eofbit); + std::streampos pos = is.tellg (); + + ref = read_value<double> (is); + + std::ios::iostate status = is.rdstate (); + if (status & std::ios::failbit) + { + is.clear (); + is.seekg (pos); + is.setstate (status & ~std::ios_base::eofbit); + } } } break;
--- a/liboctave/util/lo-utils.cc Thu Nov 24 16:16:52 2022 -0500 +++ b/liboctave/util/lo-utils.cc Fri Nov 25 08:30:30 2022 -0800 @@ -215,7 +215,10 @@ { char c2 = is.get (); if (c2 == 'f' || c2 == 'F') - val = std::numeric_limits<T>::infinity (); + { + val = std::numeric_limits<T>::infinity (); + is.peek (); // Potentially set EOF bit + } else is.setstate (std::ios::failbit); } @@ -231,7 +234,10 @@ { char c2 = is.get (); if (c2 == 'n' || c2 == 'N') - val = std::numeric_limits<T>::quiet_NaN (); + { + val = std::numeric_limits<T>::quiet_NaN (); + is.peek (); // Potentially set EOF bit + } else { val = numeric_limits<T>::NA (); @@ -290,7 +296,7 @@ is >> val; } - if (neg && ! is.fail ()) + if (neg && ! math::isnan (val) && ! is.fail ()) val = -val; } break;
--- a/test/io.tst Thu Nov 24 16:16:52 2022 -0500 +++ b/test/io.tst Fri Nov 25 08:30:30 2022 -0800 @@ -514,7 +514,7 @@ %! assert (pos, 5); ## Test NaN at EOF -%!test <63383> +%!test <*63383> %! [val, count, msg, pos] = sscanf ('2 3 n', '%f'); %! assert (val, [2; 3]); %! assert (count, 2); @@ -550,7 +550,7 @@ %! assert (pos, 10); ## Test Inf at EOF -%!test <63383> +%!test <*63383> %! [val, count, msg, pos] = sscanf ('2 3 i', '%f'); %! assert (val, [2; 3]); %! assert (count, 2); @@ -611,6 +611,60 @@ %! assert (msg, 'sscanf: format failed to match'); %! assert (pos, 5); +## Test '+' at EOF +%!test <*63383> +%! [val, count, msg, pos] = sscanf ('2 3 +', '%d'); +%! assert (val, [2; 3]); +%! assert (count, 2); +%! assert (msg, 'sscanf: format failed to match'); +%! assert (pos, 5); +%! [val, count, msg, pos] = sscanf ('2 3 +', '%f'); +%! assert (val, [2; 3]); +%! assert (count, 2); +%! assert (msg, 'sscanf: format failed to match'); +%! assert (pos, 5); + +## Test '+' within string +%!test <63383> +%! [val, count, msg, pos] = sscanf ('1 2 + 3', '%d'); +%! assert (val, [1; 2]); +%! assert (count, 2); +%! assert (msg, 'sscanf: format failed to match'); +%! assert (pos, 5); +%! [val, count, msg, pos] = sscanf ('1 2 + 3', '%f'); +%! assert (val, [1; 2]); +%! assert (count, 2); +%! assert (msg, 'sscanf: format failed to match'); +%! assert (pos, 5); + +%## Test +NA, -NA, +NAN, -NAN +%!test <*63383> +%! [val, count, msg, pos] = sscanf ('+NA -NA 1 +NAN -NAN', '%f'); +%! assert (val, [NA; NA; 1; NaN; NaN]); +%! assert (count, 5); +%! assert (msg, ''); +%! assert (pos, 20); +%! [val, count, msg, pos] = sscanf ('-NA', '%f'); +%! assert (val, NA); +%! assert (count, 1); +%! assert (msg, ''); +%! assert (pos, 4); +%! [val, count, msg, pos] = sscanf ('+NA', '%f'); +%! assert (val, NA); +%! assert (count, 1); +%! assert (msg, ''); +%! assert (pos, 4); +%! [val, count, msg, pos] = sscanf ('-NaN', '%f'); +%! assert (val, NaN); +%! assert (count, 1); +%! assert (msg, ''); +%! assert (pos, 5); +%! [val, count, msg, pos] = sscanf ('+NaN', '%f'); +%! assert (val, NaN); +%! assert (count, 1); +%! assert (msg, ''); +%! assert (pos, 5); + %!test %! [a, b, c] = sscanf ("1.2 3 foo", "%f%d%s", "C"); %! [v1, c1, m1] = sscanf ("1 2 3 4 5 6", "%d");