From a5b4ebc217afe6c31334e017d1fead4a6b8e53b2 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Wed, 15 Dec 2021 10:25:53 +0100 Subject: [PATCH] libstdc++: Poor man's case insensitive comparisons in time_get [PR71557] 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 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 | 74 +++++++++++------ .../testsuite/22_locale/time_get/get/char/71557.cc | 96 ++++++++++++++++++++++ .../22_locale/time_get/get/wchar_t/71557.cc | 96 ++++++++++++++++++++++ 3 files changed, 239 insertions(+), 27 deletions(-) create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/char/71557.cc create mode 100644 libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/71557.cc diff --git a/libstdc++-v3/include/bits/locale_facets_nonio.tcc b/libstdc++-v3/include/bits/locale_facets_nonio.tcc index 48d5e8c..3af810d 100644 --- a/libstdc++-v3/include/bits/locale_facets_nonio.tcc +++ b/libstdc++-v3/include/bits/locale_facets_nonio.tcc @@ -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 index 0000000..a0214c2 --- /dev/null +++ b/libstdc++-v3/testsuite/22_locale/time_get/get/char/71557.cc @@ -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 +// . + +#include +#include +#include +#include + +void +test01() +{ + using namespace std; + + locale loc_c = locale::classic(); + + istringstream iss; + iss.imbue(loc_c); + const time_get& tget = use_facet>(iss.getloc()); + typedef istreambuf_iterator 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 index 0000000..ed6af05 --- /dev/null +++ b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/71557.cc @@ -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 +// . + +#include +#include +#include +#include + +void +test01() +{ + using namespace std; + + locale loc_c = locale::classic(); + + wistringstream iss; + iss.imbue(loc_c); + const time_get& tget = use_facet>(iss.getloc()); + typedef istreambuf_iterator 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; +} -- 2.7.4