libstdc++: Poor man's case insensitive comparisons in time_get [PR71557]
authorJakub Jelinek <jakub@redhat.com>
Wed, 15 Dec 2021 09:25:53 +0000 (10:25 +0100)
committerJakub Jelinek <jakub@redhat.com>
Wed, 15 Dec 2021 09:25:53 +0000 (10:25 +0100)
This patch uses the same not completely correct case insensitive comparisons
as used elsewhere in the same header.  Proper comparisons that would handle
even multi-byte characters would be harder, but I don't see them implemented
in __ctype's methods.

2021-12-15  Jakub Jelinek  <jakub@redhat.com>

PR libstdc++/71557
* include/bits/locale_facets_nonio.tcc (_M_extract_via_format):
Compare characters other than format specifiers and whitespace
case insensitively.
(_M_extract_name): Compare characters case insensitively.
* testsuite/22_locale/time_get/get/char/71557.cc: New test.
* testsuite/22_locale/time_get/get/wchar_t/71557.cc: New test.

libstdc++-v3/include/bits/locale_facets_nonio.tcc
libstdc++-v3/testsuite/22_locale/time_get/get/char/71557.cc [new file with mode: 0644]
libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/71557.cc [new file with mode: 0644]

index 48d5e8c..3af810d 100644 (file)
@@ -910,7 +910,9 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
          else
            {
              // Verify format and input match, extract and discard.
-             if (__format[__i] == *__beg)
+             // TODO real case-insensitive comparison
+             if (__ctype.tolower(__format[__i]) == __ctype.tolower(*__beg)
+                 || __ctype.toupper(__format[__i]) == __ctype.toupper(*__beg))
                ++__beg;
              else
                __tmperr |= ios_base::failbit;
@@ -988,15 +990,15 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
       bool __begupdated = false;
 
       // Look for initial matches.
-      // NB: Some of the locale data is in the form of all lowercase
-      // names, and some is in the form of initially-capitalized
-      // names. Look for both.
       if (__beg != __end)
        {
          const char_type __c = *__beg;
+         // TODO real case-insensitive comparison
+         const char_type __cl = __ctype.tolower(__c);
+         const char_type __cu = __ctype.toupper(__c);
          for (size_t __i1 = 0; __i1 < __indexlen; ++__i1)
-           if (__c == __names[__i1][0]
-               || __c == __ctype.toupper(__names[__i1][0]))
+           if (__cl == __ctype.tolower(__names[__i1][0])
+               || __cu == __ctype.toupper(__names[__i1][0]))
              {
                __lengths[__nmatches]
                  = __traits_type::length(__names[__i1]);
@@ -1023,15 +1025,22 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
              bool __match_longer = false;
 
              if (__beg != __end)
-               for (size_t __i3 = 0; __i3 < __nmatches; ++__i3)
-                 {
-                   __name = __names[__matches[__i3]];
-                   if (__lengths[__i3] > __pos && (__name[__pos] == *__beg))
-                     {
-                       __match_longer = true;
-                       break;
-                     }
-                 }
+               {
+                 // TODO real case-insensitive comparison
+                 const char_type __cl = __ctype.tolower(*__beg);
+                 const char_type __cu = __ctype.toupper(*__beg);
+                 for (size_t __i3 = 0; __i3 < __nmatches; ++__i3)
+                   {
+                     __name = __names[__matches[__i3]];
+                     if (__lengths[__i3] > __pos
+                         && (__ctype.tolower(__name[__pos]) == __cl
+                             || __ctype.toupper(__name[__pos]) == __cu))
+                       {
+                         __match_longer = true;
+                         break;
+                       }
+                   }
+               }
              for (size_t __i4 = 0; __i4 < __nmatches;)
                if (__match_longer == (__lengths[__i4] == __pos))
                  {
@@ -1069,17 +1078,23 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
                }
            }
          if (__pos < __minlen && __beg != __end)
-           for (size_t __i6 = 0; __i6 < __nmatches;)
-             {
-               __name = __names[__matches[__i6]];
-               if (!(__name[__pos] == *__beg))
-                 {
-                   __matches[__i6] = __matches[--__nmatches];
-                   __lengths[__i6] = __lengths[__nmatches];
-                 }
-               else
-                 ++__i6;
-             }
+           {
+             // TODO real case-insensitive comparison
+             const char_type __cl = __ctype.tolower(*__beg);
+             const char_type __cu = __ctype.toupper(*__beg);
+             for (size_t __i6 = 0; __i6 < __nmatches;)
+               {
+                 __name = __names[__matches[__i6]];
+                 if (__ctype.tolower(__name[__pos]) != __cl
+                     && __ctype.toupper(__name[__pos]) != __cu)
+                   {
+                     __matches[__i6] = __matches[--__nmatches];
+                     __lengths[__i6] = __lengths[__nmatches];
+                   }
+                 else
+                   ++__i6;
+               }
+           }
          else
            break;
        }
@@ -1094,7 +1109,12 @@ _GLIBCXX_END_NAMESPACE_LDBL_OR_CXX11
            }
          __name = __names[__matches[0]];
          const size_t __len = __lengths[0];
-         while (__pos < __len && __beg != __end && __name[__pos] == *__beg)
+         while (__pos < __len
+                && __beg != __end
+                // TODO real case-insensitive comparison
+                && (__ctype.tolower(__name[__pos]) == __ctype.tolower(*__beg)
+                    || (__ctype.toupper(__name[__pos])
+                        == __ctype.toupper(*__beg))))
            ++__beg, (void)++__pos;
 
          if (__len == __pos)
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/char/71557.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/char/71557.cc
new file mode 100644 (file)
index 0000000..a0214c2
--- /dev/null
@@ -0,0 +1,96 @@
+// { dg-do run { target c++11 } }
+
+// Copyright (C) 2021 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/>.
+
+#include <locale>
+#include <sstream>
+#include <iterator>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using namespace std;
+
+  locale loc_c = locale::classic();
+
+  istringstream iss;
+  iss.imbue(loc_c);
+  const time_get<char>& tget = use_facet<time_get<char>>(iss.getloc());
+  typedef istreambuf_iterator<char> iter;
+  const iter end;
+
+  tm time;
+  ios_base::iostate err = ios_base::badbit;
+
+  iss.str("20:48:01 MAR 31 2016");
+  string format = "%H:%M:%S %b %d %Y";
+  auto ret = tget.get(iter(iss), end, iss, err, &time,
+                     format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2016 - 1900 );
+  VERIFY( time.tm_mon == 2 );
+  VERIFY( time.tm_mday == 31 );
+  VERIFY( time.tm_hour == 20 );
+  VERIFY( time.tm_min == 48 );
+  VERIFY( time.tm_sec == 01 );
+
+  iss.str("21:38:11 apr 30 2017");
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2017 - 1900 );
+  VERIFY( time.tm_mon == 3 );
+  VERIFY( time.tm_mday == 30 );
+  VERIFY( time.tm_hour == 21 );
+  VERIFY( time.tm_min == 38 );
+  VERIFY( time.tm_sec == 11 );
+
+  iss.str("22:28:21 mAy 29 2018");
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2018 - 1900 );
+  VERIFY( time.tm_mon == 4 );
+  VERIFY( time.tm_mday == 29 );
+  VERIFY( time.tm_hour == 22 );
+  VERIFY( time.tm_min == 28 );
+  VERIFY( time.tm_sec == 21 );
+
+  iss.str("23:18:31 JuN 28 2019");
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2019 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 28 );
+  VERIFY( time.tm_hour == 23 );
+  VERIFY( time.tm_min == 18 );
+  VERIFY( time.tm_sec == 31 );
+}
+
+int
+main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/71557.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/71557.cc
new file mode 100644 (file)
index 0000000..ed6af05
--- /dev/null
@@ -0,0 +1,96 @@
+// { dg-do run { target c++11 } }
+
+// Copyright (C) 2021 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/>.
+
+#include <locale>
+#include <sstream>
+#include <iterator>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using namespace std;
+
+  locale loc_c = locale::classic();
+
+  wistringstream iss;
+  iss.imbue(loc_c);
+  const time_get<wchar_t>& tget = use_facet<time_get<wchar_t>>(iss.getloc());
+  typedef istreambuf_iterator<wchar_t> iter;
+  const iter end;
+
+  tm time;
+  ios_base::iostate err = ios_base::badbit;
+
+  iss.str(L"20:48:01 MAR 31 2016");
+  wstring format = L"%H:%M:%S %b %d %Y";
+  auto ret = tget.get(iter(iss), end, iss, err, &time,
+                     format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2016 - 1900 );
+  VERIFY( time.tm_mon == 2 );
+  VERIFY( time.tm_mday == 31 );
+  VERIFY( time.tm_hour == 20 );
+  VERIFY( time.tm_min == 48 );
+  VERIFY( time.tm_sec == 01 );
+
+  iss.str(L"21:38:11 apr 30 2017");
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2017 - 1900 );
+  VERIFY( time.tm_mon == 3 );
+  VERIFY( time.tm_mday == 30 );
+  VERIFY( time.tm_hour == 21 );
+  VERIFY( time.tm_min == 38 );
+  VERIFY( time.tm_sec == 11 );
+
+  iss.str(L"22:28:21 mAy 29 2018");
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2018 - 1900 );
+  VERIFY( time.tm_mon == 4 );
+  VERIFY( time.tm_mday == 29 );
+  VERIFY( time.tm_hour == 22 );
+  VERIFY( time.tm_min == 28 );
+  VERIFY( time.tm_sec == 21 );
+
+  iss.str(L"23:18:31 JuN 28 2019");
+  ret = tget.get(iter(iss), end, iss, err, &time,
+                format.data(), format.data()+format.size());
+  VERIFY( err == ios_base::eofbit );
+  VERIFY( ret == end );
+  VERIFY( time.tm_year == 2019 - 1900 );
+  VERIFY( time.tm_mon == 5 );
+  VERIFY( time.tm_mday == 28 );
+  VERIFY( time.tm_hour == 23 );
+  VERIFY( time.tm_min == 18 );
+  VERIFY( time.tm_sec == 31 );
+}
+
+int
+main()
+{
+  test01();
+  return 0;
+}