libstdc++: Fix istream::ignore discarding too many chars (PR 94749)
authorJonathan Wakely <jwakely@redhat.com>
Thu, 11 Jun 2020 17:41:37 +0000 (18:41 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Thu, 11 Jun 2020 17:41:37 +0000 (18:41 +0100)
The current code assumes that if the next character in the stream is
equal to the delimiter then we stopped because we saw that delimiter,
and so discards it.  But in the testcase for the PR we stop because we
reached the maximum number of characters, and it's coincidence that the
next character equals the delimiter. We should not discard the next
character in that case.

The fix is to check that we haven't discarded __n characters already,
instead of checking whether the next character equals __delim. Because
we've already checked for EOF, if we haven't discarded __n yet then we
know we stopped because we saw the delimiter. On the other hand, if the
next character is the delimiter we don't know if that's why we stopped.

PR libstdc++/94749
* include/bits/istream.tcc (basic_istream::ignore(streamsize, CharT)):
Only discard an extra character if we didn't already reach the
maximum number.
* src/c++98/istream.cc (istream::ignore(streamsiz, char))
(wistream::ignore(streamsize, wchar_t)): Likewise.
* testsuite/27_io/basic_istream/ignore/char/94749.cc: New test.
* testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc: New test.

libstdc++-v3/include/bits/istream.tcc
libstdc++-v3/src/c++98/istream.cc
libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/94749.cc [new file with mode: 0644]
libstdc++-v3/testsuite/27_io/basic_istream/ignore/wchar_t/94749.cc [new file with mode: 0644]

index c82da56b9f9d1c06283cab6b5e57f21820530a7a..d36374c707f40deaccf353a95ef3731b6a1e7bf6 100644 (file)
@@ -601,11 +601,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
               if (traits_type::eq_int_type(__c, __eof))
                 __err |= ios_base::eofbit;
-             else if (traits_type::eq_int_type(__c, __delim))
+             else if (_M_gcount < __n) // implies __c == __delim
                {
-                 if (_M_gcount
-                     < __gnu_cxx::__numeric_traits<streamsize>::__max)
-                   ++_M_gcount;
+                 ++_M_gcount;
                  __sb->sbumpc();
                }
             }
index 79d829e23b42bf15e86b2917c48a0633992f6226..d6fee1a0d66da04be497df2ffc65462e108777de 100644 (file)
@@ -171,11 +171,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
              if (traits_type::eq_int_type(__c, __eof))
                __err |= ios_base::eofbit;
-             else if (traits_type::eq_int_type(__c, __delim))
+             else if (_M_gcount < __n) // implies __c == __delim
                {
-                 if (_M_gcount
-                     < __gnu_cxx::__numeric_traits<streamsize>::__max)
-                   ++_M_gcount;
+                 ++_M_gcount;
                  __sb->sbumpc();
                }
            }
@@ -413,11 +411,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
              if (traits_type::eq_int_type(__c, __eof))
                __err |= ios_base::eofbit;
-             else if (traits_type::eq_int_type(__c, __delim))
+             else if (_M_gcount < __n) // implies __c == __delim
                {
-                 if (_M_gcount
-                     < __gnu_cxx::__numeric_traits<streamsize>::__max)
-                   ++_M_gcount;
+                 ++_M_gcount;
                  __sb->sbumpc();
                }
            }
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
new file mode 100644 (file)
index 0000000..03b5286
--- /dev/null
@@ -0,0 +1,76 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run }
+
+// PR libstdc++/94749
+// basic_istream::ignore(n, c) discards n+1 if next character is equal to c.
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+typedef char C;
+
+void
+test01()
+{
+  std::basic_istringstream<C> s(" +   -");
+  s.ignore(1, '+');
+  VERIFY( s.get() == '+' );
+  s.ignore(3, '-');
+  VERIFY( s.get() == '-' );
+}
+
+void
+test02()
+{
+  std::basic_istringstream<C> s(".+...-");
+  s.ignore(1, '+');
+  VERIFY( s.get() == '+' );
+  s.ignore(3, '-');
+  VERIFY( s.get() == '-' );
+}
+
+void
+test03()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(" +   -");
+  s.ignore(1, '+');
+  VERIFY( s.get() == '+' );
+  s.ignore(3, '-');
+  VERIFY( s.get() == '-' );
+}
+
+void
+test04()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(".+...-");
+  s.ignore(1, '+');
+  VERIFY( s.get() == '+' );
+  s.ignore(3, '-');
+  VERIFY( s.get() == '-' );
+}
+
+
+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
new file mode 100644 (file)
index 0000000..e5ec4e7
--- /dev/null
@@ -0,0 +1,76 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run }
+
+// PR libstdc++/94749
+// basic_istream::ignore(n, c) discards n+1 if next character is equal to c.
+
+#include <sstream>
+#include <testsuite_hooks.h>
+
+typedef wchar_t C;
+
+void
+test01()
+{
+  std::basic_istringstream<C> s(L" +   -");
+  s.ignore(1, L'+');
+  VERIFY( s.get() == L'+' );
+  s.ignore(3, L'-');
+  VERIFY( s.get() == L'-' );
+}
+
+void
+test02()
+{
+  std::basic_istringstream<C> s(L".+...-");
+  s.ignore(1, L'+');
+  VERIFY( s.get() == L'+' );
+  s.ignore(3, L'-');
+  VERIFY( s.get() == L'-' );
+}
+
+void
+test03()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(L" +   -");
+  s.ignore(1, L'+');
+  VERIFY( s.get() == L'+' );
+  s.ignore(3, L'-');
+  VERIFY( s.get() == L'-' );
+}
+
+void
+test04()
+{
+  std::basic_istringstream<C, __gnu_cxx::char_traits<C> > s(L".+...-");
+  s.ignore(1, L'+');
+  VERIFY( s.get() == L'+' );
+  s.ignore(3, L'-');
+  VERIFY( s.get() == L'-' );
+}
+
+
+int
+main()
+{
+  // test01();
+  // test02();
+  test03();
+  test04();
+}