From ba8fe4b4832e30277f2e4a73b5d35b2e55074d07 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 13 Jul 2020 10:26:39 +0100 Subject: [PATCH] libstdc++: Fix istream::ignore exit conditions (PR 94749, PR 96161) My previous fix for PR 94749 did fix the reported case, so that the next character is not discarded if it happens to equal the delimiter when __n characters have already been read. But it introduced a new bug, which is that the delimiter character would *not* be discarded if the number of characters discarded is numeric_limits::max() or more before reaching the delimiter. The new bug happens because I changed the code to check _M_gcount < __n. But when __n == numeric_limits::max() that is false, and so we don't discard the delimiter. It's not sufficient to check for the delimiter when the __large_ignore condition is true, because there's an edge case where the delimiter is reached when _M_gcount == __n and so we break out of the loop without setting __large_ignore. PR 96161 is a similar bug to the original PR 94749 report, where eofbit is set after discarding __n characters if there happen to be no more characters in the stream. This patch fixes both cases (and the regression) by checking different conditions for the __n == max case and the __n < max case. For the former case, we know that we must have either reached the delimiter or EOF, and the value of _M_gcount doesn't matter (except to avoid integer overflow). For the latter case we need to check _M_gcount first and only set eofbit or discard the delimiter if it didn't reach __n. For the latter case overflow can't happen because _M_gcount <= __n < max. libstdc++-v3/ChangeLog: PR libstdc++/94749 PR libstdc++/96161 * include/bits/istream.tcc (basic_istream::ignore(streamsize)) [n == max]: Check overflow conditions on _M_gcount. Rely on the fact that either EOF or the delimiter was reached. [n < max]: Check _M_gcount < n before checking for EOF or delimiter. (basic_istream::ignore(streamsize, char_type): Likewise. * src/c++98/compatibility.cc (istream::ignore(streamsize)) (wistream::ignore(streamsize)): Likewise. * src/c++98/istream.cc (istream::ignore(streamsize, char_type)) (wistream::ignore(streamsize, char_type)): Likewise. * testsuite/27_io/basic_istream/ignore/char/94749.cc: Check that delimiter is discarded if the number of characters ignored doesn't fit in streamsize. * testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc: Likewise. * testsuite/27_io/basic_istream/ignore/char/96161.cc: New test. * testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc: New test. --- libstdc++-v3/include/bits/istream.tcc | 44 +++-- libstdc++-v3/src/c++98/compatibility.cc | 32 +++- libstdc++-v3/src/c++98/istream.cc | 56 +++++-- .../27_io/basic_istream/ignore/char/94749.cc | 151 +++++++++++++++++ .../27_io/basic_istream/ignore/char/96161.cc | 79 +++++++++ .../basic_istream/ignore/wchar_t/94749.cc | 155 +++++++++++++++++- .../basic_istream/ignore/wchar_t/96161.cc | 79 +++++++++ 7 files changed, 561 insertions(+), 35 deletions(-) create mode 100644 libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc create mode 100644 libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc diff --git a/libstdc++-v3/include/bits/istream.tcc b/libstdc++-v3/include/bits/istream.tcc index d36374c707f..5983e51873f 100644 --- a/libstdc++-v3/include/bits/istream.tcc +++ b/libstdc++-v3/include/bits/istream.tcc @@ -538,11 +538,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION break; } - if (__large_ignore) - _M_gcount = __gnu_cxx::__numeric_traits::__max; + if (__n == __gnu_cxx::__numeric_traits::__max) + { + if (__large_ignore) + _M_gcount = __gnu_cxx::__numeric_traits::__max; - if (traits_type::eq_int_type(__c, __eof)) - __err |= ios_base::eofbit; + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + } + else if (_M_gcount < __n) + { + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + } } __catch(__cxxabiv1::__forced_unwind&) { @@ -596,15 +604,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION break; } - if (__large_ignore) - _M_gcount = __gnu_cxx::__numeric_traits::__max; + if (__n == __gnu_cxx::__numeric_traits::__max) + { + if (__large_ignore) + _M_gcount = __gnu_cxx::__numeric_traits::__max; - if (traits_type::eq_int_type(__c, __eof)) - __err |= ios_base::eofbit; - else if (_M_gcount < __n) // implies __c == __delim + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + else + { + if (_M_gcount != __n) + ++_M_gcount; + __sb->sbumpc(); + } + } + else if (_M_gcount < __n) // implies __c == __delim or EOF { - ++_M_gcount; - __sb->sbumpc(); + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + else + { + ++_M_gcount; + __sb->sbumpc(); + } } } __catch(__cxxabiv1::__forced_unwind&) diff --git a/libstdc++-v3/src/c++98/compatibility.cc b/libstdc++-v3/src/c++98/compatibility.cc index 4e95b7ec68c..6024bb9dd48 100644 --- a/libstdc++-v3/src/c++98/compatibility.cc +++ b/libstdc++-v3/src/c++98/compatibility.cc @@ -107,11 +107,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION break; } - if (__large_ignore) - _M_gcount = __gnu_cxx::__numeric_traits::__max; + if (__n == __gnu_cxx::__numeric_traits::__max) + { + if (__large_ignore) + _M_gcount = __gnu_cxx::__numeric_traits::__max; - if (traits_type::eq_int_type(__c, __eof)) - __err |= ios_base::eofbit; + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + } + else if (_M_gcount < __n) + { + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + } } __catch(__cxxabiv1::__forced_unwind&) { @@ -178,11 +186,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION break; } - if (__large_ignore) - _M_gcount = __gnu_cxx::__numeric_traits::__max; + if (__n == __gnu_cxx::__numeric_traits::__max) + { + if (__large_ignore) + _M_gcount = __gnu_cxx::__numeric_traits::__max; - if (traits_type::eq_int_type(__c, __eof)) - __err |= ios_base::eofbit; + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + } + else if (_M_gcount < __n) + { + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + } } __catch(__cxxabiv1::__forced_unwind&) { diff --git a/libstdc++-v3/src/c++98/istream.cc b/libstdc++-v3/src/c++98/istream.cc index d6fee1a0d66..d2c6794be73 100644 --- a/libstdc++-v3/src/c++98/istream.cc +++ b/libstdc++-v3/src/c++98/istream.cc @@ -166,15 +166,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION break; } - if (__large_ignore) - _M_gcount = __gnu_cxx::__numeric_traits::__max; + if (__n == __gnu_cxx::__numeric_traits::__max) + { + if (__large_ignore) + _M_gcount = __gnu_cxx::__numeric_traits::__max; - if (traits_type::eq_int_type(__c, __eof)) - __err |= ios_base::eofbit; - else if (_M_gcount < __n) // implies __c == __delim + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + else + { + if (_M_gcount != __n) + ++_M_gcount; + __sb->sbumpc(); + } + } + else if (_M_gcount < __n) // implies __c == __delim or EOF { - ++_M_gcount; - __sb->sbumpc(); + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + else + { + ++_M_gcount; + __sb->sbumpc(); + } } } __catch(__cxxabiv1::__forced_unwind&) @@ -406,15 +420,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION break; } - if (__large_ignore) - _M_gcount = __gnu_cxx::__numeric_traits::__max; + if (__n == __gnu_cxx::__numeric_traits::__max) + { + if (__large_ignore) + _M_gcount = __gnu_cxx::__numeric_traits::__max; - if (traits_type::eq_int_type(__c, __eof)) - __err |= ios_base::eofbit; - else if (_M_gcount < __n) // implies __c == __delim + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + else + { + if (_M_gcount != __n) + ++_M_gcount; + __sb->sbumpc(); + } + } + else if (_M_gcount < __n) // implies __c == __delim or EOF { - ++_M_gcount; - __sb->sbumpc(); + if (traits_type::eq_int_type(__c, __eof)) + __err |= ios_base::eofbit; + else + { + ++_M_gcount; + __sb->sbumpc(); + } } } __catch(__cxxabiv1::__forced_unwind&) diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc index 03b5286b00e..32b604d5b15 100644 --- a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc +++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc @@ -16,11 +16,13 @@ // . // { dg-do run } +// { dg-options "-DSIMULATOR_TEST" { target simulator } } // PR libstdc++/94749 // basic_istream::ignore(n, c) discards n+1 if next character is equal to c. #include +#include #include typedef char C; @@ -30,8 +32,10 @@ test01() { std::basic_istringstream s(" + -"); s.ignore(1, '+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == '+' ); s.ignore(3, '-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == '-' ); } @@ -40,8 +44,10 @@ test02() { std::basic_istringstream s(".+...-"); s.ignore(1, '+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == '+' ); s.ignore(3, '-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == '-' ); } @@ -50,8 +56,10 @@ test03() { std::basic_istringstream > s(" + -"); s.ignore(1, '+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == '+' ); s.ignore(3, '-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == '-' ); } @@ -60,11 +68,150 @@ test04() { std::basic_istringstream > s(".+...-"); s.ignore(1, '+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == '+' ); s.ignore(3, '-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == '-' ); } +// The original fix for PR libstdc++/94749 failed to discard the delimiter +// if it occurred after numeric_limits::max() had been seen. +// This streambuf will keep filling the get area with zero bytes until +// almost numeric_limits::max() characters have been read, +// and then return one more buffer that has "123" at its end. +template +struct buff : std::basic_streambuf +{ + typedef typename T::char_type char_type; + typedef typename T::int_type int_type; + typedef std::streamsize streamsize; + typedef std::numeric_limits limits; + + buff() : count(0), buf() { } + + int_type underflow() + { + // Number of characters left until we overflow the counter + const streamsize headroom = limits::max() - count; + + if (headroom == 0) + return T::eof(); + + if (bufsz < headroom) + { + this->setg(buf, buf, buf + bufsz); + count += bufsz; + } + else + { + // write "123" across the 2GB boundary + buf[headroom-1] = '1'; + buf[headroom+0] = '2'; + buf[headroom+1] = '3'; + this->setg(buf, buf, buf + headroom + 2); + count = limits::max(); + } + + return buf[0]; + } + + streamsize count; + + static const streamsize bufsz = 2048 << limits::digits10; + char_type buf[bufsz + 2]; +}; + +void +test05() +{ + // Not possible to overflow 64-bit streamsize in reasonable time. + if (std::numeric_limits::digits > 32) + return; + + typedef std::char_traits T; + + std::basic_istream in(new buff); + + in.ignore(std::numeric_limits::max(), '1'); + VERIFY(in.good()); + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == '2'); + VERIFY(in.get() == '3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), '2'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == '3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), '3'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), '4'); + VERIFY(in.eof()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(nullptr); +} + +void +test06() +{ + if (std::numeric_limits::digits > 32) + return; + + typedef __gnu_cxx::char_traits T; + + std::basic_istream in(new buff); + + in.ignore(std::numeric_limits::max(), '1'); + VERIFY(in.good()); + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == '2'); + VERIFY(in.get() == '3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), '2'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == '3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), '3'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), '4'); + VERIFY(in.eof()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(nullptr); +} int main() @@ -73,4 +220,8 @@ main() test02(); test03(); test04(); + test05(); +#ifndef SIMULATOR_TEST + test06(); +#endif } diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc new file mode 100644 index 00000000000..c19e5f9e758 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/96161.cc @@ -0,0 +1,79 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run } + +// PR libstdc++/96161 +// basic_istream::ignore sets eofbit too soon + +#include +#include +#include + +typedef char C; + +void +test01() +{ + std::basic_istringstream s(" "); + s.ignore(2, '+'); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == std::char_traits::eof() ); + VERIFY( s.eof() ); +} + +void +test02() +{ + std::basic_istringstream s(" "); + s.ignore(2); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == std::char_traits::eof() ); + VERIFY( s.eof() ); +} + +void +test03() +{ + std::basic_istringstream > s(" "); + s.ignore(2, '+'); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == __gnu_cxx::char_traits::eof() ); + VERIFY( s.eof() ); +} + +void +test04() +{ + std::basic_istringstream > s(" "); + s.ignore(2); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == __gnu_cxx::char_traits::eof() ); + VERIFY( s.eof() ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc index e5ec4e7d697..23d3a0ad170 100644 --- a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc +++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc @@ -16,11 +16,13 @@ // . // { dg-do run } +// { dg-options "-DSIMULATOR_TEST" { target simulator } } // PR libstdc++/94749 // basic_istream::ignore(n, c) discards n+1 if next character is equal to c. #include +#include #include typedef wchar_t C; @@ -30,8 +32,10 @@ test01() { std::basic_istringstream s(L" + -"); s.ignore(1, L'+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == L'+' ); s.ignore(3, L'-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == L'-' ); } @@ -40,8 +44,10 @@ test02() { std::basic_istringstream s(L".+...-"); s.ignore(1, L'+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == L'+' ); s.ignore(3, L'-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == L'-' ); } @@ -50,8 +56,10 @@ test03() { std::basic_istringstream > s(L" + -"); s.ignore(1, L'+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == L'+' ); s.ignore(3, L'-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == L'-' ); } @@ -60,17 +68,160 @@ test04() { std::basic_istringstream > s(L".+...-"); s.ignore(1, L'+'); + VERIFY( s.gcount() == 1 ); VERIFY( s.get() == L'+' ); s.ignore(3, L'-'); + VERIFY( s.gcount() == 3 ); VERIFY( s.get() == L'-' ); } +// The original fix for PR libstdc++/94749 failed to discard the delimiter +// if it occurred after numeric_limits::max() had been seen. +// This streambuf will keep filling the get area with zero bytes until +// almost numeric_limits::max() characters have been read, +// and then return one more buffer that has L"123" at its end. +template +struct buff : std::basic_streambuf +{ + typedef typename T::char_type char_type; + typedef typename T::int_type int_type; + typedef std::streamsize streamsize; + typedef std::numeric_limits limits; + + buff() : count(0), buf() { } + + int_type underflow() + { + // Number of characters left until we overflow the counter + const streamsize headroom = limits::max() - count; + + if (headroom == 0) + return T::eof(); + + if (bufsz < headroom) + { + this->setg(buf, buf, buf + bufsz); + count += bufsz; + } + else + { + // write L"123" across the 2GB boundary + buf[headroom-1] = L'1'; + buf[headroom+0] = L'2'; + buf[headroom+1] = L'3'; + this->setg(buf, buf, buf + headroom + 2); + count = limits::max(); + } + + return buf[0]; + } + + streamsize count; + + static const streamsize bufsz = 2048 << limits::digits10; + char_type buf[bufsz + 2]; +}; + +void +test05() +{ + // Not possible to overflow 64-bit streamsize in reasonable time. + if (std::numeric_limits::digits > 32) + return; + + typedef std::char_traits T; + + std::basic_istream in(new buff); + + in.ignore(std::numeric_limits::max(), L'1'); + VERIFY(in.good()); + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == L'2'); + VERIFY(in.get() == L'3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), L'2'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == L'3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), L'3'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), L'4'); + VERIFY(in.eof()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(nullptr); +} + +void +test06() +{ + if (std::numeric_limits::digits > 32) + return; + + typedef __gnu_cxx::char_traits T; + + std::basic_istream in(new buff); + + in.ignore(std::numeric_limits::max(), L'1'); + VERIFY(in.good()); + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == L'2'); + VERIFY(in.get() == L'3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), L'2'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == L'3'); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), L'3'); + VERIFY(in.good()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(new buff); + + in.ignore(std::numeric_limits::max(), L'4'); + VERIFY(in.eof()); + // The standard doesn't say what gcount() should return in this case: + VERIFY(in.gcount() == std::numeric_limits::max()); + VERIFY(in.get() == T::eof()); + + delete in.rdbuf(nullptr); +} int main() { - // test01(); - // test02(); + test01(); + test02(); test03(); test04(); + test05(); +#ifndef SIMULATOR_TEST + test06(); +#endif } diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc new file mode 100644 index 00000000000..64234426d15 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/96161.cc @@ -0,0 +1,79 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run } + +// PR libstdc++/96161 +// basic_istream::ignore sets eofbit too soon + +#include +#include +#include + +typedef wchar_t C; + +void +test01() +{ + std::basic_istringstream s(L" "); + s.ignore(2, L'+'); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == std::char_traits::eof() ); + VERIFY( s.eof() ); +} + +void +test02() +{ + std::basic_istringstream s(L" "); + s.ignore(2); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == std::char_traits::eof() ); + VERIFY( s.eof() ); +} + +void +test03() +{ + std::basic_istringstream > s(L" "); + s.ignore(2, L'+'); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == __gnu_cxx::char_traits::eof() ); + VERIFY( s.eof() ); +} + +void +test04() +{ + std::basic_istringstream > s(L" "); + s.ignore(2); + VERIFY( s.gcount() == 2 ); + VERIFY( s.good() ); + VERIFY( s.get() == __gnu_cxx::char_traits::eof() ); + VERIFY( s.eof() ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); +} -- 2.30.2