[RFC,v3,06/11] Provide backward compatibility for strftime family (bug 10871).

Message ID 392970184.118201.1476749184039@poczta.nazwa.pl
State Superseded
Headers

Commit Message

Rafal Luzynski Oct. 18, 2016, 12:06 a.m. UTC
  As %OB format specifier has been added to strftime/wcsftime
family of functions backward compatibility implementation must be
provided for older binaries which assume that %B returns
a month name in the nominative case.

[BZ #10871]
* include/time.h: Declare __strftime_l_common.
* include/wchar.h: Declare __wcsftime_l_common.
* time/Versions (libc: GLIBC_2.25): New strftime(_l) and wcsftime(_l) added.
* time/strftime.c: Provide backward compatible version.
* time/strftime_l.c: Likewise.
* time/wcsftime.c: Likewise.
* time/wcsftime_l.c: Likewise.
---
 include/time.h    |  9 +++++++++
 include/wchar.h   |  9 +++++++++
 time/Versions     |  3 +++
 time/strftime.c   | 20 +++++++++++++++++---
 time/strftime_l.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
 time/wcsftime.c   | 22 ++++++++++++++++++----
 time/wcsftime_l.c | 20 +++++++++++++++++++-
 7 files changed, 115 insertions(+), 18 deletions(-)
  

Comments

Joseph Myers Oct. 18, 2016, 3:08 p.m. UTC | #1
__strftime_l and __wcsftime_l are also public exports, also used by 
libstdc++, so also need compatibility.
  
Rafal Luzynski Oct. 18, 2016, 11:20 p.m. UTC | #2
18.10.2016 17:08 Joseph Myers <joseph@codesourcery.com> wrote:
>
>
> __strftime_l and __wcsftime_l are also public exports, also used by
> libstdc++, so also need compatibility.

Technically I can provide it, together with __nl_langinfo_l, but
are you sure it will be useful for libstdc++?  My concern is that
libstdc++ will have to provide its own backward compatibility
explicitly.  Otherwise an executable dynamically linked against
libstdc++ will have no way to tell whether it wants a new or old
version of underlying functions.

My other concern is that no other function starting with double
underscore provides multiple versions, at least on x86_64
architecture.  I've found several which provide only backward
compatible versions but no current version, I guess that's the
way to mark them as deprecated.

Regards,

Rafal
  
Joseph Myers Oct. 18, 2016, 11:40 p.m. UTC | #3
On Wed, 19 Oct 2016, Rafal Luzynski wrote:

> 18.10.2016 17:08 Joseph Myers <joseph@codesourcery.com> wrote:
> >
> >
> > __strftime_l and __wcsftime_l are also public exports, also used by
> > libstdc++, so also need compatibility.
> 
> Technically I can provide it, together with __nl_langinfo_l, but
> are you sure it will be useful for libstdc++?  My concern is that

The presumption is that public functions need to stay compatible unless 
shown otherwise.  You could analyse what libstdc++ does, but that wouldn't 
resolve the question of any other libraries that might use these functions 
for namespace reasons and also need such compatibility (cf today's 
discussion of how we should systematically provide such 
implementation-namespace versions of more functions for use by system 
libraries).
  

Patch

diff --git a/include/time.h b/include/time.h
index 684ceb8..80ac40f 100644
--- a/include/time.h
+++ b/include/time.h
@@ -10,6 +10,15 @@  extern __typeof (strftime_l) __strftime_l;
 libc_hidden_proto (__strftime_l)
 extern __typeof (strptime_l) __strptime_l;
 
+/* Backward compatibility function: feature_OB argument specifies
+   whether or not %OB format specifier should be implemented.  */
+extern size_t __strftime_l_common (char *__restrict __s, size_t __maxsize,
+				   const char *__restrict __format,
+				   const struct tm *__restrict __tp,
+				   const int feature_OB,
+				   __locale_t __loc) __THROW;
+libc_hidden_proto (__strftime_l_common)
+
 libc_hidden_proto (time)
 libc_hidden_proto (asctime)
 libc_hidden_proto (mktime)
diff --git a/include/wchar.h b/include/wchar.h
index 6272130..495ad91 100644
--- a/include/wchar.h
+++ b/include/wchar.h
@@ -25,6 +25,15 @@  libc_hidden_proto (__wcstof_l)
 libc_hidden_proto (__wcstold_l)
 libc_hidden_proto (__wcsftime_l)
 
+/* Backward compatibility function: feature_OB argument specifies
+   whether or not %OB format specifier should be implemented.  */
+extern size_t __wcsftime_l_common (wchar_t *__restrict __s, size_t __maxsize,
+				   const wchar_t *__restrict __format,
+				   const struct tm *__restrict __tp,
+				   const int feature_OB,
+				   __locale_t __loc) __THROW;
+libc_hidden_proto (__wcsftime_l_common)
+
 
 extern double __wcstod_internal (const wchar_t *__restrict __nptr,
 				 wchar_t **__restrict __endptr, int __group)
diff --git a/time/Versions b/time/Versions
index fd83818..03fbc0f 100644
--- a/time/Versions
+++ b/time/Versions
@@ -65,4 +65,7 @@  libc {
   GLIBC_2.16 {
     timespec_get;
   }
+  GLIBC_2.25 {
+    strftime; strftime_l; wcsftime; wcsftime_l;
+  }
 }
diff --git a/time/strftime.c b/time/strftime.c
index 92150d9..45131d2 100644
--- a/time/strftime.c
+++ b/time/strftime.c
@@ -17,11 +17,25 @@ 
 
 #include <time.h>
 #include <locale/localeinfo.h>
+#include <shlib-compat.h>
 
 
 size_t
-strftime (char *s, size_t maxsize, const char *format, const struct tm *tp)
+__strftime (char *s, size_t maxsize, const char *format, const struct tm *tp)
 {
-  return __strftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE);
+  return __strftime_l_common (s, maxsize, format, tp, 1, _NL_CURRENT_LOCALE);
 }
-libc_hidden_def (strftime)
+versioned_symbol (libc, __strftime, strftime, GLIBC_2_25);
+libc_hidden_ver (__strftime, strftime)
+
+
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
+size_t
+attribute_compat_text_section
+__strftime_compat (char *s, size_t maxsize, const char *format,
+		   const struct tm *tp)
+{
+  return __strftime_l_common (s, maxsize, format, tp, 0, _NL_CURRENT_LOCALE);
+}
+compat_symbol (libc, __strftime_compat, strftime, GLIBC_2_0);
+#endif
diff --git a/time/strftime_l.c b/time/strftime_l.c
index 4d54e23..7f12463 100644
--- a/time/strftime_l.c
+++ b/time/strftime_l.c
@@ -56,6 +56,8 @@ 
 extern char *tzname[];
 #endif
 
+#include <shlib-compat.h>
+
 /* Do multibyte processing if multibytes are supported, unless
    multibyte sequences are safe in formats.  Multibyte sequences are
    safe if they cannot contain byte sequences that look like format
@@ -279,15 +281,19 @@  static const CHAR_T zeroes[16] = /* "0000000000000000" */
    function gets as an additional argument the locale which has to be
    used.  To access the values we have to redefine the _NL_CURRENT
    macro.  */
-# define strftime		__strftime_l
-# define wcsftime		__wcsftime_l
+# define strftime		__strftime_l_common
+# define wcsftime		__wcsftime_l_common
 # undef _NL_CURRENT
 # define _NL_CURRENT(category, item) \
   (current->values[_NL_ITEM_INDEX (item)].string)
+# define FEATURE_OB_PARAM , int feature_OB
+# define FEATURE_OB_ARG , feature_OB
 # define LOCALE_PARAM , __locale_t loc
 # define LOCALE_ARG , loc
 # define HELPER_LOCALE_ARG  , current
 #else
+# define FEATURE_OB_PARAM
+# define FEATURE_OB_ARG
 # define LOCALE_PARAM
 # define LOCALE_ARG
 # ifdef _LIBC
@@ -435,6 +441,7 @@  static CHAR_T const month_name[][10] =
 static size_t __strftime_internal (CHAR_T *, size_t, const CHAR_T *,
 				   const struct tm *, bool *
 				   ut_argument_spec
+				   FEATURE_OB_PARAM
 				   LOCALE_PARAM) __THROW;
 
 /* Write information from TP into S according to the format
@@ -446,7 +453,7 @@  static size_t __strftime_internal (CHAR_T *, size_t, const
CHAR_T *,
 
 size_t
 my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
-	     const struct tm *tp ut_argument_spec LOCALE_PARAM)
+	     const struct tm *tp ut_argument_spec FEATURE_OB_PARAM LOCALE_PARAM)
 {
 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
   /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
@@ -457,7 +464,7 @@  my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T
*format,
 #endif
   bool tzset_called = false;
   return __strftime_internal (s, maxsize, format, tp, &tzset_called
-			      ut_argument LOCALE_ARG);
+			      ut_argument FEATURE_OB_ARG LOCALE_ARG);
 }
 #ifdef _LIBC
 libc_hidden_def (my_strftime)
@@ -466,10 +473,12 @@  libc_hidden_def (my_strftime)
 static size_t
 __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
 		     const struct tm *tp, bool *tzset_called
-		     ut_argument_spec LOCALE_PARAM)
+		     ut_argument_spec FEATURE_OB_PARAM LOCALE_PARAM)
 {
 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
   struct __locale_data *const current = loc->__locales[LC_TIME];
+#else
+# define feature_OB 1
 #endif
 
   int hour12 = tp->tm_hour;
@@ -781,6 +790,8 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T
*format,
 	case L_('B'):
 	  if (modifier == L_('E'))
 	    goto bad_format;
+	  if (!feature_OB && modifier == L_('O'))
+	    goto bad_format;
 	  if (change_case)
 	    {
 	      to_uppcase = 1;
@@ -788,7 +799,7 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T
*format,
 	    }
 #if defined _NL_CURRENT || !HAVE_STRFTIME
 	  /* Use f_altmonth only if f_altmonth is provided.  */
-	  if (f_altmonth[0] && modifier == L_('O'))
+	  if (f_altmonth[0] && (!feature_OB || modifier == L_('O')))
 	    cpy (STRLEN (f_altmonth), f_altmonth);
 	  else
 	    cpy (STRLEN (f_month), f_month);
@@ -820,10 +831,10 @@  __strftime_internal (CHAR_T *s, size_t maxsize, const
CHAR_T *format,
 	    CHAR_T *old_start = p;
 	    size_t len = __strftime_internal (NULL, (size_t) -1, subfmt,
 					      tp, tzset_called ut_argument
-					      LOCALE_ARG);
+					      FEATURE_OB_ARG LOCALE_ARG);
 	    add (len, __strftime_internal (p, maxsize - i, subfmt,
 					   tp, tzset_called ut_argument
-					   LOCALE_ARG));
+					   FEATURE_OB_ARG LOCALE_ARG));
 
 	    if (to_uppcase)
 	      while (old_start < p)
@@ -1424,10 +1435,29 @@  size_t
 emacs_strftime (char *s, size_t maxsize, const char *format,
 		const struct tm *tp)
 {
-  return my_strftime (s, maxsize, format, tp, 0);
+  return my_strftime (s, maxsize, format, tp, 1, 0);
 }
 #endif
 
 #if defined _LIBC && !defined COMPILE_WIDE
-weak_alias (__strftime_l, strftime_l)
+size_t
+__strftime_l (char *s, size_t maxsize, const char *format,
+	      const struct tm *tp, __locale_t loc)
+{
+  return my_strftime (s, maxsize, format, tp, 1, loc);
+}
+libc_hidden_def (__strftime_l)
+versioned_symbol (libc, __strftime_l, strftime_l, GLIBC_2_25);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_3, GLIBC_2_25)
+size_t
+attribute_compat_text_section
+__strftime_l_compat (char *s, size_t maxsize, const char *format,
+		     const struct tm *tp, __locale_t loc)
+{
+  return my_strftime (s, maxsize, format, tp, 0, loc);
+}
+compat_symbol (libc, __strftime_l_compat, strftime_l, GLIBC_2_3);
+# endif
+
 #endif
diff --git a/time/wcsftime.c b/time/wcsftime.c
index a8f06f1..ee83624 100644
--- a/time/wcsftime.c
+++ b/time/wcsftime.c
@@ -17,12 +17,26 @@ 
 
 #include <wchar.h>
 #include <locale/localeinfo.h>
+#include <shlib-compat.h>
 
 
 size_t
-wcsftime (wchar_t *s, size_t maxsize, const wchar_t *format,
-	  const struct tm *tp)
+__wcsftime (wchar_t *s, size_t maxsize, const wchar_t *format,
+	    const struct tm *tp)
 {
-  return __wcsftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE);
+  return __wcsftime_l_common (s, maxsize, format, tp, 1, _NL_CURRENT_LOCALE);
 }
-libc_hidden_def (wcsftime)
+versioned_symbol (libc, __wcsftime, wcsftime, GLIBC_2_25);
+libc_hidden_ver (__wcsftime, wcsftime)
+
+
+#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_25)
+size_t
+attribute_compat_text_section
+__wcsftime_compat (wchar_t *s, size_t maxsize, const wchar_t *format,
+		   const struct tm *tp)
+{
+  return __wcsftime_l_common (s, maxsize, format, tp, 0, _NL_CURRENT_LOCALE);
+}
+compat_symbol (libc, __wcsftime_compat, wcsftime, GLIBC_2_2);
+#endif
diff --git a/time/wcsftime_l.c b/time/wcsftime_l.c
index f771417..879eb7b 100644
--- a/time/wcsftime_l.c
+++ b/time/wcsftime_l.c
@@ -22,4 +22,22 @@ 
 #define COMPILE_WIDE	1
 #include "strftime_l.c"
 
-weak_alias (__wcsftime_l, wcsftime_l)
+size_t
+__wcsftime_l (wchar_t *s, size_t maxsize, const wchar_t *format,
+	      const struct tm *tp, __locale_t loc)
+{
+  return my_strftime (s, maxsize, format, tp, 1, loc);
+}
+libc_hidden_def (__wcsftime_l)
+versioned_symbol (libc, __wcsftime_l, wcsftime_l, GLIBC_2_25);
+
+#if SHLIB_COMPAT (libc, GLIBC_2_3, GLIBC_2_25)
+size_t
+attribute_compat_text_section
+__wcsftime_l_compat (wchar_t *s, size_t maxsize, const wchar_t *format,
+		     const struct tm *tp, __locale_t loc)
+{
+  return my_strftime (s, maxsize, format, tp, 0, loc);
+}
+compat_symbol (libc, __wcsftime_l_compat, wcsftime_l, GLIBC_2_3);
+#endif