From patchwork Thu Oct 11 21:07:57 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: TAMUKI Shoichi X-Patchwork-Id: 982741 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=sourceware.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=libc-alpha-return-96381-incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linet.gr.jp Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b="k22XehXp"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42WNsr4sPVz9s8J for ; Fri, 12 Oct 2018 08:10:40 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:message-id:from:date:to:subject:mime-version :content-type; q=dns; s=default; b=KIdwwOGpx6dK5sDU+T7PZSDFME7mL uzEHC8AFJUv4SXqE2YVbLrPRmY9ciTcl86b0VVJdnt9v/GkRCxYXhm0itDQgBQ/W er4FKV0tdIBusXq5GaSLRBS9thkwyYQ7w+0qKvwI3ycHbA5/vOUZBAJI2XxjGH3/ C6r8ysmGoVgQs8= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:message-id:from:date:to:subject:mime-version :content-type; s=default; bh=epudnXXEpfs3FVyfjptW+UodIqk=; b=k22 XehXpDpWD+myLMAn8ir7tr2qydri2kdpMi689itU7/DLo1tZkDaUCNMyLLM6dRZB asGLt+usQ6aEe6E/r4/Vlmhmcrwjo89QdiQCt1CAzly5HAiBzyfnjuMIhwo43WZ8 qjQGf4hI5LVBwZm0cB/K8uouRTI0GCeKagUqgPSU= Received: (qmail 8219 invoked by alias); 11 Oct 2018 21:10:34 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 8197 invoked by uid 89); 11 Oct 2018 21:10:33 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KAM_SHORT, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 spammy=Thailand, thailand, minute, era X-HELO: mail.linet.jp Message-Id: <201810112107.AA04060@tamuki.linet.gr.jp> From: TAMUKI Shoichi Date: Fri, 12 Oct 2018 06:07:57 +0900 To: libc-alpha@sourceware.org Subject: [PATCH] Improve the width of alternate representation for year in strftime [BZ #23758] MIME-Version: 1.0 The Japanese era name is scheduled to be changed on May 1, 2019. Prior to this, change the alternate representation for year in strftime to pad the number with zero to keep it constant width, so that prevent the trouble we saw in the past from becoming obvious again from the year after the era name changes onward. Since only one Japanese era name is used by each emperor's reign, it is rare that the year ends in one digit or lasts more than three digits. In addition, the width of month, day, hour, minute, and second is 2, so adjust the width of year the same as them, the whole display balance is improved. Therefore, it would be reasonable to change the width padding with zero of %Ey default to 2. In glibc, besides ja_JP locale, the locales currently using the conversion specifier above are lo_LA (Laos) and th_TH (Thailand). In these locales, they use the Buddhist era. The Buddhist era is a value obtained by adding 543 to the Christian era, so it is not affected by this change of the conversion specifier %Ey. Furthermore, for the output string of the conversion specifier %EY, an optional flag is given to the conversion specifier so that it can be also used the current non-padding format, and the padding format can be controlled. To achieve this, when an optional flag is given to the conversion specifier %EY, processing is performed as the flag to be given to %Ey included in the combined conversion specifier. ChangeLog: [BZ #23758] * time/Makefile (tests): Add tst-strftime2. * time/strftime_l.c (STRCPY, STRSTR, MEMMOVE): New macros. (add): Free subfmt2 before return if using malloc. (subfmt2, using_malloc): New variables in __strftime_internal. (__strftime_internal): Change the width padding with zero of %Ey default to 2. If an optional flag ('_' or '-') is specified to %EY, delegate the flag to %Ey in subformat, and then free subfmt2 after add call if using malloc. * time/tst-strftime2.c: New file. --- time/Makefile | 2 +- time/strftime_l.c | 53 ++++++++++++++++++-- time/tst-strftime2.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 time/tst-strftime2.c diff --git a/time/Makefile b/time/Makefile index ec3e39dcea..6dc2acceaa 100644 --- a/time/Makefile +++ b/time/Makefile @@ -43,7 +43,7 @@ tests := test_time clocktest tst-posixtz tst-strptime tst_wcsftime \ tst-getdate tst-mktime tst-mktime2 tst-ftime_l tst-strftime \ tst-mktime3 tst-strptime2 bug-asctime bug-asctime_r bug-mktime1 \ tst-strptime3 bug-getdate1 tst-strptime-whitespace tst-ftime \ - tst-tzname tst-y2039 + tst-tzname tst-y2039 tst-strftime2 include ../Rules diff --git a/time/strftime_l.c b/time/strftime_l.c index c71f9f47a9..0030b631fa 100644 --- a/time/strftime_l.c +++ b/time/strftime_l.c @@ -100,6 +100,9 @@ extern char *tzname[]; # define MEMCPY(d, s, n) __wmemcpy (d, s, n) # define STRLEN(s) __wcslen (s) +# define STRCPY wcscpy +# define STRSTR wcsstr +# define MEMMOVE wmemmove #else # define CHAR_T char @@ -114,6 +117,9 @@ extern char *tzname[]; # define MEMCPY(d, s, n) memcpy ((d), (s), (n)) # endif # define STRLEN(s) strlen (s) +# define STRCPY strcpy +# define STRSTR strstr +# define MEMMOVE memmove # ifdef _LIBC # define MEMPCPY(d, s, n) __mempcpy (d, s, n) @@ -233,7 +239,11 @@ static const CHAR_T zeroes[16] = /* "0000000000000000" */ int _delta = width - _n; \ int _incr = _n + (_delta > 0 ? _delta : 0); \ if ((size_t) _incr >= maxsize - i) \ - return 0; \ + { \ + if (using_malloc) \ + free (subfmt2); \ + return 0; \ + } \ if (p) \ { \ if (_delta > 0) \ @@ -574,6 +584,8 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, int to_uppcase = 0; int change_case = 0; int format_char; + CHAR_T *subfmt2; + bool using_malloc = false; #if DO_MULTIBYTE && !defined COMPILE_WIDE switch (*f) @@ -820,7 +832,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, if (modifier == L_('O')) goto bad_format; #ifdef _NL_CURRENT - if (! (modifier == 'E' + if (! (modifier == L_('E') && (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_D_T_FMT))) @@ -843,6 +855,11 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, add (len, __strftime_internal (p, maxsize - i, subfmt, tp, tzset_called ut_argument LOCALE_ARG)); + if (using_malloc) + { + free (subfmt2); + using_malloc = false; + } if (to_uppcase) while (old_start < p) @@ -917,7 +934,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, #ifdef _NL_CURRENT if (! (modifier == L_('E') && (*(subfmt = - (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT))) + (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_D_FMT))) != L_('\0')))) subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT)); goto subformat; @@ -1262,7 +1279,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, DO_NUMBER (1, tp->tm_wday); case L_('Y'): - if (modifier == 'E') + if (modifier == L_('E')) { #if HAVE_STRUCT_ERA_ENTRY struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); @@ -1273,6 +1290,32 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, # else subfmt = era->era_format; # endif + if (pad != 0) + { + CHAR_T *flag; + + if (__libc_use_alloca ((STRLEN (subfmt) + 2) + * sizeof (CHAR_T))) + subfmt2 = (CHAR_T *) alloca ((STRLEN (subfmt) + 2) + * sizeof (CHAR_T)); + else + { + if ((subfmt2 = + (CHAR_T *) malloc ((STRLEN (subfmt) + 2) + * sizeof (CHAR_T))) == NULL) + goto subformat; + using_malloc = true; + } + STRCPY (subfmt2, subfmt); + if ((flag = STRSTR (subfmt2, L_("%Ey"))) != NULL) + { + MEMMOVE (flag + 2 * sizeof (CHAR_T), + flag + sizeof (CHAR_T), STRLEN (flag)); + *(flag + sizeof (CHAR_T)) = (CHAR_T) pad; + subfmt = subfmt2; + pad = 0; + } + } goto subformat; } #else @@ -1294,7 +1337,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, if (era) { int delta = tp->tm_year - era->start_date[0]; - DO_NUMBER (1, (era->offset + DO_NUMBER (2, (era->offset + delta * era->absolute_direction)); } #else diff --git a/time/tst-strftime2.c b/time/tst-strftime2.c new file mode 100644 index 0000000000..3e7ddfe9ea --- /dev/null +++ b/time/tst-strftime2.c @@ -0,0 +1,134 @@ +/* Verify the behavior of strftime on alternate representation for year. + + Copyright (C) 2013-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static const char *locales[] = { "ja_JP.UTF-8", "lo_LA.UTF-8", "th_TH.UTF-8" }; +#define nlocales (sizeof (locales) / sizeof (locales[0])) + +static const char *formats[] = { "%EY", "%_EY", "%-EY" }; +#define nformats (sizeof (formats) / sizeof (formats[0])) + +static const struct +{ + const int d, m, y; +} dates[] = + { + { 1, 3, 88 }, + { 7, 0, 89 }, + { 8, 0, 89 }, + { 1, 3, 90 }, + { 1, 3, 97 }, + { 1, 3, 98 } + }; +#define ndates (sizeof (dates) / sizeof (dates[0])) + +static char ref[nlocales][nformats][ndates][100]; + +static void +mkreftable (void) +{ + int i, j, k; + char era[10]; + static const int yrj[] = { 63, 64, 1, 2, 9, 10 }; + static const int yrb[] = { 2531, 2532, 2532, 2533, 2540, 2541 }; + + for (i = 0; i < nlocales; i++) + for (j = 0; j < nformats; j++) + for (k = 0; k < ndates; k++) + { + if (i == 0) + { + sprintf (era, "%s", (k < 2) ? "\xe6\x98\xad\xe5\x92\x8c" + : "\xe5\xb9\xb3\xe6\x88\x90"); + if (yrj[k] == 1) + sprintf (ref[i][j][k], "%s\xe5\x85\x83\xe5\xb9\xb4", era); + else + { + if (j == 0) + sprintf (ref[i][j][k], "%s%02d\xe5\xb9\xb4", era, yrj[k]); + else if (j == 1) + sprintf (ref[i][j][k], "%s%2d\xe5\xb9\xb4", era, yrj[k]); + else + sprintf (ref[i][j][k], "%s%d\xe5\xb9\xb4", era, yrj[k]); + } + } + else if (i == 1) + { + sprintf (era, "\xe0\xba\x9e\x2e\xe0\xba\xaa\x2e "); + sprintf (ref[i][j][k], "%s%d", era, yrb[k]); + } + else + { + sprintf (era, "\xe0\xb8\x9e\x2e\xe0\xb8\xa8\x2e "); + sprintf (ref[i][j][k], "%s%d", era, yrb[k]); + } + } +} + +static int +do_test (void) +{ + int i, j, k, result = 0; + struct tm ttm; + char date[11], buf[100]; + size_t r, e; + + mkreftable (); + for (i = 0; i < nlocales; i++) + { + if (setlocale (LC_ALL, locales[i]) == NULL) + { + printf ("locale %s does not exist, skipping...\n", locales[i]); + continue; + } + printf ("[%s]\n", locales[i]); + for (j = 0; j < nformats; j++) + { + for (k = 0; k < ndates; k++) + { + ttm.tm_mday = dates[k].d; + ttm.tm_mon = dates[k].m; + ttm.tm_year = dates[k].y; + strftime (date, sizeof (date), "%F", &ttm); + r = strftime (buf, sizeof (buf), formats[j], &ttm); + e = strlen (ref[i][j][k]); + printf ("%s\t\"%s\"\t\"%s\"", date, formats[j], buf); + if (strcmp (buf, ref[i][j][k]) != 0) + { + printf ("\tshould be \"%s\"", ref[i][j][k]); + if (r != e) + printf ("\tgot: %zu, expected: %zu", r, e); + result = 1; + } + else + printf ("\tOK"); + putchar ('\n'); + } + putchar ('\n'); + } + } + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c"