Mercurial > octave
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));