source: trunk/essentials/sys-apps/gawk/missing_d/strftime.c

Last change on this file was 3076, checked in by bird, 18 years ago

gawk 3.1.5

File size: 35.2 KB
Line 
1/* Copyright (C) 1991-1999, 2000, 2001, 2002, 2003
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301 USA. */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#ifdef _LIBC
25# define HAVE_LIMITS_H 1
26# define HAVE_MBLEN 1
27# define HAVE_MBRLEN 1
28# define HAVE_STRUCT_ERA_ENTRY 1
29# define HAVE_TM_GMTOFF 1
30# define HAVE_TM_ZONE 1
31# define HAVE_TZNAME 1
32# define HAVE_TZSET 1
33# define MULTIBYTE_IS_FORMAT_SAFE 1
34# define STDC_HEADERS 1
35# include "../locale/localeinfo.h"
36#endif
37
38#if defined emacs && !defined HAVE_BCOPY
39# define HAVE_MEMCPY 1
40#endif
41
42#include <ctype.h>
43#ifdef TIME_T_IN_SYS_TYPES
44#include <sys/types.h> /* Some systems define `time_t' here. */
45#endif
46
47#ifdef TIME_WITH_SYS_TIME
48# include <sys/time.h>
49# include <time.h>
50#else
51# ifdef HAVE_SYS_TIME_H
52# include <sys/time.h>
53# else
54# include <time.h>
55# endif
56#endif
57#if HAVE_TZNAME
58extern char *tzname[];
59#endif
60
61/* Do multibyte processing if multibytes are supported, unless
62 multibyte sequences are safe in formats. Multibyte sequences are
63 safe if they cannot contain byte sequences that look like format
64 conversion specifications. The GNU C Library uses UTF8 multibyte
65 encoding, which is safe for formats, but strftime.c can be used
66 with other C libraries that use unsafe encodings. */
67#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
68
69#if DO_MULTIBYTE
70# if HAVE_MBRLEN
71# include <wchar.h>
72# else
73 /* Simulate mbrlen with mblen as best we can. */
74# define mbstate_t int
75# define mbrlen(s, n, ps) mblen (s, n)
76# define mbsinit(ps) (*(ps) == 0)
77# endif
78 static const mbstate_t mbstate_zero;
79#endif
80
81#if HAVE_LIMITS_H
82# include <limits.h>
83#endif
84
85#if STDC_HEADERS
86# include <stddef.h>
87# include <stdlib.h>
88# include <string.h>
89#else
90# ifndef HAVE_MEMCPY
91# define memcpy(d, s, n) bcopy ((s), (d), (n))
92# endif
93#endif
94
95#ifdef COMPILE_WIDE
96# include <endian.h>
97# define CHAR_T wchar_t
98# define UCHAR_T unsigned int
99# define L_(Str) L##Str
100# define NLW(Sym) _NL_W##Sym
101
102# define MEMCPY(d, s, n) __wmemcpy (d, s, n)
103# define STRLEN(s) __wcslen (s)
104
105#else
106# define CHAR_T char
107# define UCHAR_T unsigned char
108# define L_(Str) Str
109# define NLW(Sym) Sym
110
111# if !defined STDC_HEADERS && !defined HAVE_MEMCPY
112# define MEMCPY(d, s, n) bcopy ((s), (d), (n))
113# else
114# define MEMCPY(d, s, n) memcpy ((d), (s), (n))
115# endif
116# define STRLEN(s) strlen (s)
117
118# ifdef _LIBC
119# define MEMPCPY(d, s, n) __mempcpy (d, s, n)
120# else
121# ifndef HAVE_MEMPCPY
122# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
123# endif
124# endif
125#endif
126
127#ifndef __P
128# if defined __GNUC__ || (defined __STDC__ && __STDC__)
129# define __P(args) args
130# else
131# define __P(args) ()
132# endif /* GCC. */
133#endif /* Not __P. */
134
135#ifndef PTR
136# ifdef __STDC__
137# define PTR void *
138# else
139# define PTR char *
140# endif
141#endif
142
143#ifndef CHAR_BIT
144# define CHAR_BIT 8
145#endif
146
147#ifndef NULL
148# define NULL 0
149#endif
150
151/* Test for checking whether a given type is signed or not.
152 Some compilers issue a diagnostic about suspicious construct for
153 a test that will always fail when comparing a value that can't be
154 negative against 0 using `<' or `<=' operator. */
155/* #define TYPE_SIGNED(t) ((t) -1 < 0) */
156#define TYPE_SIGNED(t) ((t) -1 < 1)
157
158#ifndef INT_STRLEN_BOUND
159/* Bound on length of the string representing an integer value of type t.
160 Subtract one for the sign bit if t is signed;
161 302 / 1000 is log10 (2) rounded up;
162 add one for integer division truncation;
163 add one more for a minus sign if t is signed. */
164#define INT_STRLEN_BOUND(t) \
165 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
166#endif
167
168#define TM_YEAR_BASE 1900
169
170#ifndef __isleap
171/* Nonzero if YEAR is a leap year (every 4 years,
172 except every 100th isn't, and every 400th is). */
173# define __isleap(year) \
174 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
175#endif
176
177
178#ifdef _LIBC
179# define my_strftime_gmtime_r __gmtime_r
180# define my_strftime_localtime_r __localtime_r
181# define tzname __tzname
182# define tzset __tzset
183#else
184
185/* If we're a strftime substitute in a GNU program, then prefer gmtime
186 to gmtime_r, since many gmtime_r implementations are buggy.
187 Similarly for localtime_r. */
188
189# if ! HAVE_TM_GMTOFF
190static struct tm *my_strftime_gmtime_r __P ((const time_t *, struct tm *));
191static struct tm *
192my_strftime_gmtime_r (t, tp)
193 const time_t *t;
194 struct tm *tp;
195{
196 struct tm *l = gmtime (t);
197 if (! l)
198 return 0;
199 *tp = *l;
200 return tp;
201}
202# endif /* ! HAVE_TM_GMTOFF */
203
204static struct tm *my_strftime_localtime_r __P ((const time_t *, struct tm *));
205static struct tm *
206my_strftime_localtime_r (t, tp)
207 const time_t *t;
208 struct tm *tp;
209{
210 struct tm *l = localtime (t);
211 if (! l)
212 return 0;
213 *tp = *l;
214 return tp;
215}
216#endif /* ! defined _LIBC */
217
218
219#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
220/* Some systems lack the `memset' function and we don't want to
221 introduce additional dependencies. */
222/* The SGI compiler reportedly barfs on the trailing null
223 if we use a string constant as the initializer. 28 June 1997, rms. */
224static const CHAR_T spaces[16] = /* " " */
225{
226 L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),
227 L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' ')
228};
229static const CHAR_T zeroes[16] = /* "0000000000000000" */
230{
231 L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),
232 L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0')
233};
234
235# define memset_space(P, Len) \
236 do { \
237 int _len = (Len); \
238 \
239 do \
240 { \
241 int _this = _len > 16 ? 16 : _len; \
242 (P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T)); \
243 _len -= _this; \
244 } \
245 while (_len > 0); \
246 } while (0)
247
248# define memset_zero(P, Len) \
249 do { \
250 int _len = (Len); \
251 \
252 do \
253 { \
254 int _this = _len > 16 ? 16 : _len; \
255 (P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T)); \
256 _len -= _this; \
257 } \
258 while (_len > 0); \
259 } while (0)
260#else
261# ifdef COMPILE_WIDE
262# define memset_space(P, Len) (wmemset ((P), L' ', (Len)), (P) += (Len))
263# define memset_zero(P, Len) (wmemset ((P), L'0', (Len)), (P) += (Len))
264# else
265# define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
266# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
267# endif
268#endif
269
270#define add(n, f) \
271 do \
272 { \
273 int _n = (n); \
274 int _delta = width - _n; \
275 int _incr = _n + (_delta > 0 ? _delta : 0); \
276 if ((size_t) _incr >= maxsize - i) \
277 return 0; \
278 if (p) \
279 { \
280 if (_delta > 0) \
281 { \
282 if (pad == L_('0')) \
283 memset_zero (p, _delta); \
284 else \
285 memset_space (p, _delta); \
286 } \
287 f; \
288 p += _n; \
289 } \
290 i += _incr; \
291 } while (0)
292
293#define cpy(n, s) \
294 add ((n), \
295 if (to_lowcase) \
296 memcpy_lowcase (p, (s), _n LOCALE_ARG); \
297 else if (to_uppcase) \
298 memcpy_uppcase (p, (s), _n LOCALE_ARG); \
299 else \
300 MEMCPY ((PTR) p, (const PTR) (s), _n))
301
302#ifdef COMPILE_WIDE
303# ifndef USE_IN_EXTENDED_LOCALE_MODEL
304# undef __mbsrtowcs_l
305# define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
306# endif
307# define widen(os, ws, l) \
308 { \
309 mbstate_t __st; \
310 const char *__s = os; \
311 memset (&__st, '\0', sizeof (__st)); \
312 l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc); \
313 ws = alloca ((l + 1) * sizeof (wchar_t)); \
314 (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc); \
315 }
316#endif
317
318/* For gawk */
319#undef TOLOWER
320#undef TOUPPER
321#undef ISDIGIT
322
323#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
324/* We use this code also for the extended locale handling where the
325 function gets as an additional argument the locale which has to be
326 used. To access the values we have to redefine the _NL_CURRENT
327 macro. */
328# define strftime __strftime_l
329# define wcsftime __wcsftime_l
330# undef _NL_CURRENT
331# define _NL_CURRENT(category, item) \
332 (current->values[_NL_ITEM_INDEX (item)].string)
333# define LOCALE_PARAM , loc
334# define LOCALE_ARG , loc
335# define LOCALE_PARAM_DECL __locale_t loc;
336# define LOCALE_PARAM_PROTO , __locale_t loc
337# define HELPER_LOCALE_ARG , current
338#else
339# define LOCALE_PARAM
340# define LOCALE_PARAM_PROTO
341# define LOCALE_ARG
342# define LOCALE_PARAM_DECL
343# ifdef _LIBC
344# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
345# else
346# define HELPER_LOCALE_ARG
347# endif
348#endif
349
350#ifdef COMPILE_WIDE
351# ifdef USE_IN_EXTENDED_LOCALE_MODEL
352# define TOUPPER(Ch, L) __towupper_l (Ch, L)
353# define TOLOWER(Ch, L) __towlower_l (Ch, L)
354# else
355# define TOUPPER(Ch, L) towupper (Ch)
356# define TOLOWER(Ch, L) towlower (Ch)
357# endif
358#else
359# ifdef _LIBC
360# ifdef USE_IN_EXTENDED_LOCALE_MODEL
361# define TOUPPER(Ch, L) __toupper_l (Ch, L)
362# define TOLOWER(Ch, L) __tolower_l (Ch, L)
363# else
364# define TOUPPER(Ch, L) toupper (Ch)
365# define TOLOWER(Ch, L) tolower (Ch)
366# endif
367# else
368# define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
369# define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
370# endif
371#endif
372/* We don't use `isdigit' here since the locale dependent
373 interpretation is not what we want here. We only need to accept
374 the arabic digits in the ASCII range. One day there is perhaps a
375 more reliable way to accept other sets of digits. */
376#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
377
378static CHAR_T *memcpy_lowcase __P ((CHAR_T *dest, const CHAR_T *src,
379 size_t len LOCALE_PARAM_PROTO));
380
381static CHAR_T *
382memcpy_lowcase (dest, src, len LOCALE_PARAM)
383 CHAR_T *dest;
384 const CHAR_T *src;
385 size_t len;
386 LOCALE_PARAM_DECL
387{
388 while (len-- > 0)
389 dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
390 return dest;
391}
392
393static CHAR_T *memcpy_uppcase __P ((CHAR_T *dest, const CHAR_T *src,
394 size_t len LOCALE_PARAM_PROTO));
395
396static CHAR_T *
397memcpy_uppcase (dest, src, len LOCALE_PARAM)
398 CHAR_T *dest;
399 const CHAR_T *src;
400 size_t len;
401 LOCALE_PARAM_DECL
402{
403 while (len-- > 0)
404 dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
405 return dest;
406}
407
408
409#if ! HAVE_TM_GMTOFF
410/* Yield the difference between *A and *B,
411 measured in seconds, ignoring leap seconds. */
412# define tm_diff ftime_tm_diff
413static int tm_diff __P ((const struct tm *, const struct tm *));
414static int
415tm_diff (a, b)
416 const struct tm *a;
417 const struct tm *b;
418{
419 /* Compute intervening leap days correctly even if year is negative.
420 Take care to avoid int overflow in leap day calculations,
421 but it's OK to assume that A and B are close to each other. */
422 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
423 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
424 int a100 = a4 / 25 - (a4 % 25 < 0);
425 int b100 = b4 / 25 - (b4 % 25 < 0);
426 int a400 = a100 >> 2;
427 int b400 = b100 >> 2;
428 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
429 int years = a->tm_year - b->tm_year;
430 int days = (365 * years + intervening_leap_days
431 + (a->tm_yday - b->tm_yday));
432 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
433 + (a->tm_min - b->tm_min))
434 + (a->tm_sec - b->tm_sec));
435}
436#endif /* ! HAVE_TM_GMTOFF */
437
438
439
440/* The number of days from the first day of the first ISO week of this
441 year to the year day YDAY with week day WDAY. ISO weeks start on
442 Monday; the first ISO week has the year's first Thursday. YDAY may
443 be as small as YDAY_MINIMUM. */
444#define ISO_WEEK_START_WDAY 1 /* Monday */
445#define ISO_WEEK1_WDAY 4 /* Thursday */
446#define YDAY_MINIMUM (-366)
447static int iso_week_days __P ((int, int));
448#ifdef __GNUC__
449__inline__
450#endif
451static int
452iso_week_days (yday, wday)
453 int yday;
454 int wday;
455{
456 /* Add enough to the first operand of % to make it nonnegative. */
457 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
458 return (yday
459 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
460 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
461}
462
463
464#if !(defined _NL_CURRENT || HAVE_STRFTIME)
465static CHAR_T const weekday_name[][10] =
466 {
467 L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"),
468 L_("Thursday"), L_("Friday"), L_("Saturday")
469 };
470static CHAR_T const month_name[][10] =
471 {
472 L_("January"), L_("February"), L_("March"), L_("April"), L_("May"),
473 L_("June"), L_("July"), L_("August"), L_("September"), L_("October"),
474 L_("November"), L_("December")
475 };
476#endif
477
478
479#ifdef emacs
480# define my_strftime emacs_strftimeu
481# define ut_argument , ut
482# define ut_argument_spec int ut;
483# define ut_argument_spec_iso , int ut
484#else
485# ifdef COMPILE_WIDE
486# define my_strftime wcsftime
487# define nl_get_alt_digit _nl_get_walt_digit
488# else
489# define my_strftime strftime
490# define nl_get_alt_digit _nl_get_alt_digit
491# endif
492# define ut_argument
493# define ut_argument_spec
494# define ut_argument_spec_iso
495/* We don't have this information in general. */
496# define ut 0
497#endif
498
499#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
500 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
501 Work around this bug by copying *tp before it might be munged. */
502 size_t _strftime_copytm __P ((char *, size_t, const char *,
503 const struct tm * ut_argument_spec_iso));
504 size_t
505 my_strftime (s, maxsize, format, tp ut_argument)
506 CHAR_T *s;
507 size_t maxsize;
508 const CHAR_T *format;
509 const struct tm *tp;
510 ut_argument_spec
511 {
512 struct tm tmcopy;
513 tmcopy = *tp;
514 return _strftime_copytm (s, maxsize, format, &tmcopy ut_argument);
515 }
516# undef my_strftime
517# define my_strftime _strftime_copytm
518#endif
519
520
521/* Write information from TP into S according to the format
522 string FORMAT, writing no more that MAXSIZE characters
523 (including the terminating '\0') and returning number of
524 characters written. If S is NULL, nothing will be written
525 anywhere, so to determine how many characters would be
526 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
527size_t
528my_strftime (s, maxsize, format, tp ut_argument LOCALE_PARAM)
529 CHAR_T *s;
530 size_t maxsize;
531 const CHAR_T *format;
532 const struct tm *tp;
533 ut_argument_spec
534 LOCALE_PARAM_DECL
535{
536#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
537 struct locale_data *const current = loc->__locales[LC_TIME];
538#endif
539
540 int hour12 = tp->tm_hour;
541#ifdef _NL_CURRENT
542 /* We cannot make the following values variables since we must delay
543 the evaluation of these values until really needed since some
544 expressions might not be valid in every situation. The `struct tm'
545 might be generated by a strptime() call that initialized
546 only a few elements. Dereference the pointers only if the format
547 requires this. Then it is ok to fail if the pointers are invalid. */
548# define a_wkday \
549 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday))
550# define f_wkday \
551 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday))
552# define a_month \
553 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon))
554# define f_month \
555 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon))
556# define ampm \
557 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \
558 ? NLW(PM_STR) : NLW(AM_STR)))
559
560# define aw_len STRLEN (a_wkday)
561# define am_len STRLEN (a_month)
562# define ap_len STRLEN (ampm)
563#else
564# if !HAVE_STRFTIME
565# define f_wkday (weekday_name[tp->tm_wday])
566# define f_month (month_name[tp->tm_mon])
567# define a_wkday f_wkday
568# define a_month f_month
569# define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
570
571 size_t aw_len = 3;
572 size_t am_len = 3;
573 size_t ap_len = 2;
574# endif
575#endif
576 const char *zone;
577 size_t i = 0;
578 CHAR_T *p = s;
579 const CHAR_T *f;
580#if DO_MULTIBYTE && !defined COMPILE_WIDE
581 const char *format_end = NULL;
582#endif
583
584 zone = NULL;
585#if HAVE_TM_ZONE
586 /* The POSIX test suite assumes that setting
587 the environment variable TZ to a new value before calling strftime()
588 will influence the result (the %Z format) even if the information in
589 TP is computed with a totally different time zone.
590 This is bogus: though POSIX allows bad behavior like this,
591 POSIX does not require it. Do the right thing instead. */
592 zone = (const char *) tp->tm_zone;
593#endif
594#if HAVE_TZNAME
595 if (ut)
596 {
597 if (! (zone && *zone))
598 zone = "GMT";
599 }
600 else
601 {
602 /* POSIX.1 requires that local time zone information is used as
603 though strftime called tzset. */
604# if HAVE_TZSET
605 tzset ();
606# endif
607 }
608#endif
609
610 if (hour12 > 12)
611 hour12 -= 12;
612 else
613 if (hour12 == 0)
614 hour12 = 12;
615
616 for (f = format; *f != '\0'; ++f)
617 {
618 int pad = 0; /* Padding for number ('-', '_', or 0). */
619 int modifier; /* Field modifier ('E', 'O', or 0). */
620 int digits; /* Max digits for numeric format. */
621 int number_value; /* Numeric value to be printed. */
622 int negative_number; /* 1 if the number is negative. */
623 const CHAR_T *subfmt;
624 CHAR_T *bufp;
625 CHAR_T buf[1 + (sizeof (int) < sizeof (time_t)
626 ? INT_STRLEN_BOUND (time_t)
627 : INT_STRLEN_BOUND (int))];
628 int width = -1;
629 int to_lowcase = 0;
630 int to_uppcase = 0;
631 int change_case = 0;
632 int format_char;
633
634#if DO_MULTIBYTE && !defined COMPILE_WIDE
635 switch (*f)
636 {
637 case L_('%'):
638 break;
639
640 case L_('\b'): case L_('\t'): case L_('\n'):
641 case L_('\v'): case L_('\f'): case L_('\r'):
642 case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
643 case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
644 case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
645 case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
646 case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
647 case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
648 case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
649 case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
650 case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
651 case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
652 case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
653 case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
654 case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
655 case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
656 case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
657 case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
658 case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
659 case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
660 case L_('~'):
661 /* The C Standard requires these 98 characters (plus '%') to
662 be in the basic execution character set. None of these
663 characters can start a multibyte sequence, so they need
664 not be analyzed further. */
665 add (1, *p = *f);
666 continue;
667
668 default:
669 /* Copy this multibyte sequence until we reach its end, find
670 an error, or come back to the initial shift state. */
671 {
672 mbstate_t mbstate = mbstate_zero;
673 size_t len = 0;
674 size_t fsize;
675
676 if (! format_end)
677 format_end = f + strlen (f) + 1;
678 fsize = format_end - f;
679
680 do
681 {
682 size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
683
684 if (bytes == 0)
685 break;
686
687 if (bytes == (size_t) -2)
688 {
689 len += strlen (f + len);
690 break;
691 }
692
693 if (bytes == (size_t) -1)
694 {
695 len++;
696 break;
697 }
698
699 len += bytes;
700 }
701 while (! mbsinit (&mbstate));
702
703 cpy (len, f);
704 f += len - 1;
705 continue;
706 }
707 }
708
709#else /* ! DO_MULTIBYTE */
710
711 /* Either multibyte encodings are not supported, they are
712 safe for formats, so any non-'%' byte can be copied through,
713 or this is the wide character version. */
714 if (*f != L_('%'))
715 {
716 add (1, *p = *f);
717 continue;
718 }
719
720#endif /* ! DO_MULTIBYTE */
721
722 /* Check for flags that can modify a format. */
723 while (1)
724 {
725 switch (*++f)
726 {
727 /* This influences the number formats. */
728 case L_('_'):
729 case L_('-'):
730 case L_('0'):
731 pad = *f;
732 continue;
733
734 /* This changes textual output. */
735 case L_('^'):
736 to_uppcase = 1;
737 continue;
738 case L_('#'):
739 change_case = 1;
740 continue;
741
742 default:
743 break;
744 }
745 break;
746 }
747
748 /* As a GNU extension we allow to specify the field width. */
749 if (ISDIGIT (*f))
750 {
751 width = 0;
752 do
753 {
754 if (width > INT_MAX / 10
755 || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10))
756 /* Avoid overflow. */
757 width = INT_MAX;
758 else
759 {
760 width *= 10;
761 width += *f - L_('0');
762 }
763 ++f;
764 }
765 while (ISDIGIT (*f));
766 }
767
768 /* Check for modifiers. */
769 switch (*f)
770 {
771 case L_('E'):
772 case L_('O'):
773 modifier = *f++;
774 break;
775
776 default:
777 modifier = 0;
778 break;
779 }
780
781 /* Now do the specified format. */
782 format_char = *f;
783 switch (format_char)
784 {
785#define DO_NUMBER(d, v) \
786 digits = d > width ? d : width; \
787 number_value = v; goto do_number
788#define DO_NUMBER_SPACEPAD(d, v) \
789 digits = d > width ? d : width; \
790 number_value = v; goto do_number_spacepad
791
792 case L_('%'):
793 if (modifier != 0)
794 goto bad_format;
795 add (1, *p = *f);
796 break;
797
798 case L_('a'):
799 if (modifier != 0)
800 goto bad_format;
801 if (change_case)
802 {
803 to_uppcase = 1;
804 to_lowcase = 0;
805 }
806#if defined _NL_CURRENT || !HAVE_STRFTIME
807 cpy (aw_len, a_wkday);
808 break;
809#else
810 goto underlying_strftime;
811#endif
812
813 case 'A':
814 if (modifier != 0)
815 goto bad_format;
816 if (change_case)
817 {
818 to_uppcase = 1;
819 to_lowcase = 0;
820 }
821#if defined _NL_CURRENT || !HAVE_STRFTIME
822 cpy (STRLEN (f_wkday), f_wkday);
823 break;
824#else
825 goto underlying_strftime;
826#endif
827
828 case L_('b'):
829 case L_('h'):
830 if (change_case)
831 {
832 to_uppcase = 1;
833 to_lowcase = 0;
834 }
835 if (modifier != 0)
836 goto bad_format;
837#if defined _NL_CURRENT || !HAVE_STRFTIME
838 cpy (am_len, a_month);
839 break;
840#else
841 goto underlying_strftime;
842#endif
843
844 case L_('B'):
845 if (modifier != 0)
846 goto bad_format;
847 if (change_case)
848 {
849 to_uppcase = 1;
850 to_lowcase = 0;
851 }
852#if defined _NL_CURRENT || !HAVE_STRFTIME
853 cpy (STRLEN (f_month), f_month);
854 break;
855#else
856 goto underlying_strftime;
857#endif
858
859 case L_('c'):
860 if (modifier == L_('O'))
861 goto bad_format;
862#ifdef _NL_CURRENT
863 if (! (modifier == 'E'
864 && (*(subfmt =
865 (const CHAR_T *) _NL_CURRENT (LC_TIME,
866 NLW(ERA_D_T_FMT)))
867 != '\0')))
868 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
869#else
870# if HAVE_STRFTIME
871 goto underlying_strftime;
872# else
873 subfmt = L_("%a %b %e %H:%M:%S %Y");
874# endif
875#endif
876
877 subformat:
878 {
879 CHAR_T *old_start = p;
880 size_t len = my_strftime (NULL, (size_t) -1, subfmt,
881 tp ut_argument LOCALE_ARG);
882 add (len, my_strftime (p, maxsize - i, subfmt,
883 tp ut_argument LOCALE_ARG));
884
885 if (to_uppcase)
886 while (old_start < p)
887 {
888 *old_start = TOUPPER ((UCHAR_T) *old_start, loc);
889 ++old_start;
890 }
891 }
892 break;
893
894#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
895 underlying_strftime:
896 {
897 /* The relevant information is available only via the
898 underlying strftime implementation, so use that. */
899 char ufmt[4];
900 char *u = ufmt;
901 char ubuf[1024]; /* enough for any single format in practice */
902 size_t len;
903 /* Make sure we're calling the actual underlying strftime.
904 In some cases, config.h contains something like
905 "#define strftime rpl_strftime". */
906# ifdef strftime
907# undef strftime
908 size_t strftime ();
909# endif
910
911 *u++ = '%';
912 if (modifier != 0)
913 *u++ = modifier;
914 *u++ = format_char;
915 *u = '\0';
916 len = strftime (ubuf, sizeof ubuf, ufmt, tp);
917 if (len == 0 && ubuf[0] != '\0')
918 return 0;
919 cpy (len, ubuf);
920 }
921 break;
922#endif
923
924 case L_('C'):
925 if (modifier == L_('O'))
926 goto bad_format;
927 if (modifier == L_('E'))
928 {
929#if HAVE_STRUCT_ERA_ENTRY
930 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
931 if (era)
932 {
933# ifdef COMPILE_WIDE
934 size_t len = __wcslen (era->era_wname);
935 cpy (len, era->era_wname);
936# else
937 size_t len = strlen (era->era_name);
938 cpy (len, era->era_name);
939# endif
940 break;
941 }
942#else
943# if HAVE_STRFTIME
944 goto underlying_strftime;
945# endif
946#endif
947 }
948
949 {
950 int year = tp->tm_year + TM_YEAR_BASE;
951 DO_NUMBER (1, year / 100 - (year % 100 < 0));
952 }
953
954 case L_('x'):
955 if (modifier == L_('O'))
956 goto bad_format;
957#ifdef _NL_CURRENT
958 if (! (modifier == L_('E')
959 && (*(subfmt =
960 (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
961 != L_('\0'))))
962 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
963 goto subformat;
964#else
965# if HAVE_STRFTIME
966 goto underlying_strftime;
967# else
968 /* Fall through. */
969# endif
970#endif
971 case L_('D'):
972 if (modifier != 0)
973 goto bad_format;
974 subfmt = L_("%m/%d/%y");
975 goto subformat;
976
977 case L_('d'):
978 if (modifier == L_('E'))
979 goto bad_format;
980
981 DO_NUMBER (2, tp->tm_mday);
982
983 case L_('e'):
984 if (modifier == L_('E'))
985 goto bad_format;
986
987 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
988
989 /* All numeric formats set DIGITS and NUMBER_VALUE and then
990 jump to one of these two labels. */
991
992 do_number_spacepad:
993 /* Force `_' flag unless overwritten by `0' flag. */
994 if (pad != L_('0'))
995 pad = L_('_');
996
997 do_number:
998 /* Format the number according to the MODIFIER flag. */
999
1000 if (modifier == L_('O') && 0 <= number_value)
1001 {
1002#ifdef _NL_CURRENT
1003 /* Get the locale specific alternate representation of
1004 the number NUMBER_VALUE. If none exist NULL is returned. */
1005 const CHAR_T *cp = nl_get_alt_digit (number_value
1006 HELPER_LOCALE_ARG);
1007
1008 if (cp != NULL)
1009 {
1010 size_t digitlen = STRLEN (cp);
1011 if (digitlen != 0)
1012 {
1013 cpy (digitlen, cp);
1014 break;
1015 }
1016 }
1017#else
1018# if HAVE_STRFTIME
1019 goto underlying_strftime;
1020# endif
1021#endif
1022 }
1023 {
1024 unsigned int u = number_value;
1025
1026 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1027 negative_number = number_value < 0;
1028
1029 if (negative_number)
1030 u = -u;
1031
1032 do
1033 *--bufp = u % 10 + L_('0');
1034 while ((u /= 10) != 0);
1035 }
1036
1037 do_number_sign_and_padding:
1038 if (negative_number)
1039 *--bufp = L_('-');
1040
1041 if (pad != L_('-'))
1042 {
1043 int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0]))
1044 - bufp);
1045
1046 if (padding > 0)
1047 {
1048 if (pad == L_('_'))
1049 {
1050 if ((size_t) padding >= maxsize - i)
1051 return 0;
1052
1053 if (p)
1054 memset_space (p, padding);
1055 i += padding;
1056 width = width > padding ? width - padding : 0;
1057 }
1058 else
1059 {
1060 if ((size_t) digits >= maxsize - i)
1061 return 0;
1062
1063 if (negative_number)
1064 {
1065 ++bufp;
1066
1067 if (p)
1068 *p++ = L_('-');
1069 ++i;
1070 }
1071
1072 if (p)
1073 memset_zero (p, padding);
1074 i += padding;
1075 width = 0;
1076 }
1077 }
1078 }
1079
1080 cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
1081 break;
1082
1083 case L_('F'):
1084 if (modifier != 0)
1085 goto bad_format;
1086 subfmt = L_("%Y-%m-%d");
1087 goto subformat;
1088
1089 case L_('H'):
1090 if (modifier == L_('E'))
1091 goto bad_format;
1092
1093 DO_NUMBER (2, tp->tm_hour);
1094
1095 case L_('I'):
1096 if (modifier == L_('E'))
1097 goto bad_format;
1098
1099 DO_NUMBER (2, hour12);
1100
1101 case L_('k'): /* GNU extension. */
1102 if (modifier == L_('E'))
1103 goto bad_format;
1104
1105 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
1106
1107 case L_('l'): /* GNU extension. */
1108 if (modifier == L_('E'))
1109 goto bad_format;
1110
1111 DO_NUMBER_SPACEPAD (2, hour12);
1112
1113 case L_('j'):
1114 if (modifier == L_('E'))
1115 goto bad_format;
1116
1117 DO_NUMBER (3, 1 + tp->tm_yday);
1118
1119 case L_('M'):
1120 if (modifier == L_('E'))
1121 goto bad_format;
1122
1123 DO_NUMBER (2, tp->tm_min);
1124
1125 case L_('m'):
1126 if (modifier == L_('E'))
1127 goto bad_format;
1128
1129 DO_NUMBER (2, tp->tm_mon + 1);
1130
1131 case L_('n'):
1132 add (1, *p = L_('\n'));
1133 break;
1134
1135 case L_('P'):
1136 to_lowcase = 1;
1137#if !defined _NL_CURRENT && HAVE_STRFTIME
1138 format_char = L_('p');
1139#endif
1140 /* FALLTHROUGH */
1141
1142 case L_('p'):
1143 if (change_case)
1144 {
1145 to_uppcase = 0;
1146 to_lowcase = 1;
1147 }
1148#if defined _NL_CURRENT || !HAVE_STRFTIME
1149 cpy (ap_len, ampm);
1150 break;
1151#else
1152 goto underlying_strftime;
1153#endif
1154
1155 case L_('R'):
1156 subfmt = L_("%H:%M");
1157 goto subformat;
1158
1159 case L_('r'):
1160#ifdef _NL_CURRENT
1161 if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
1162 NLW(T_FMT_AMPM)))
1163 == L_('\0'))
1164#endif
1165 subfmt = L_("%I:%M:%S %p");
1166 goto subformat;
1167
1168 case L_('S'):
1169 if (modifier == L_('E'))
1170 goto bad_format;
1171
1172 DO_NUMBER (2, tp->tm_sec);
1173
1174 case L_('s'): /* GNU extension. */
1175 {
1176 struct tm ltm;
1177 time_t t;
1178
1179 ltm = *tp;
1180 t = mktime (&ltm);
1181
1182 /* Generate string value for T using time_t arithmetic;
1183 this works even if sizeof (long) < sizeof (time_t). */
1184
1185 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1186#ifndef TIME_T_UNSIGNED
1187 negative_number = t < 0;
1188#endif
1189
1190 do
1191 {
1192 int d = t % 10;
1193 t /= 10;
1194
1195#ifndef TIME_T_UNSIGNED
1196 if (negative_number)
1197 {
1198 d = -d;
1199
1200 /* Adjust if division truncates to minus infinity. */
1201 if (0 < -1 % 10 && d < 0)
1202 {
1203 t++;
1204 d += 10;
1205 }
1206 }
1207#endif
1208
1209 *--bufp = d + L_('0');
1210 }
1211 while (t != 0);
1212
1213 digits = 1;
1214 goto do_number_sign_and_padding;
1215 }
1216
1217 case L_('X'):
1218 if (modifier == L_('O'))
1219 goto bad_format;
1220#ifdef _NL_CURRENT
1221 if (! (modifier == L_('E')
1222 && (*(subfmt =
1223 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
1224 != L_('\0'))))
1225 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
1226 goto subformat;
1227#else
1228# if HAVE_STRFTIME
1229 goto underlying_strftime;
1230# else
1231 /* Fall through. */
1232# endif
1233#endif
1234 case L_('T'):
1235 subfmt = L_("%H:%M:%S");
1236 goto subformat;
1237
1238 case L_('t'):
1239 add (1, *p = L_('\t'));
1240 break;
1241
1242 case L_('u'):
1243 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1244
1245 case L_('U'):
1246 if (modifier == L_('E'))
1247 goto bad_format;
1248
1249 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1250
1251 case L_('V'):
1252 case L_('g'):
1253 case L_('G'):
1254 if (modifier == L_('E'))
1255 goto bad_format;
1256 {
1257 int year = tp->tm_year + TM_YEAR_BASE;
1258 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1259
1260 if (days < 0)
1261 {
1262 /* This ISO week belongs to the previous year. */
1263 year--;
1264 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
1265 tp->tm_wday);
1266 }
1267 else
1268 {
1269 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1270 tp->tm_wday);
1271 if (0 <= d)
1272 {
1273 /* This ISO week belongs to the next year. */
1274 year++;
1275 days = d;
1276 }
1277 }
1278
1279 switch (*f)
1280 {
1281 case L_('g'):
1282 DO_NUMBER (2, (year % 100 + 100) % 100);
1283
1284 case L_('G'):
1285 DO_NUMBER (1, year);
1286
1287 default:
1288 DO_NUMBER (2, days / 7 + 1);
1289 }
1290 }
1291
1292 case L_('W'):
1293 if (modifier == L_('E'))
1294 goto bad_format;
1295
1296 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1297
1298 case L_('w'):
1299 if (modifier == L_('E'))
1300 goto bad_format;
1301
1302 DO_NUMBER (1, tp->tm_wday);
1303
1304 case L_('Y'):
1305 if (modifier == 'E')
1306 {
1307#if HAVE_STRUCT_ERA_ENTRY
1308 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1309 if (era)
1310 {
1311# ifdef COMPILE_WIDE
1312 subfmt = era->era_wformat;
1313# else
1314 subfmt = era->era_format;
1315# endif
1316 goto subformat;
1317 }
1318#else
1319# if HAVE_STRFTIME
1320 goto underlying_strftime;
1321# endif
1322#endif
1323 }
1324 if (modifier == L_('O'))
1325 goto bad_format;
1326 else
1327 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
1328
1329 case L_('y'):
1330 if (modifier == L_('E'))
1331 {
1332#if HAVE_STRUCT_ERA_ENTRY
1333 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1334 if (era)
1335 {
1336 int delta = tp->tm_year - era->start_date[0];
1337 DO_NUMBER (1, (era->offset
1338 + delta * era->absolute_direction));
1339 }
1340#else
1341# if HAVE_STRFTIME
1342 goto underlying_strftime;
1343# endif
1344#endif
1345 }
1346 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
1347
1348 case L_('Z'):
1349 if (change_case)
1350 {
1351 to_uppcase = 0;
1352 to_lowcase = 1;
1353 }
1354
1355#if HAVE_TZNAME
1356 /* The tzset() call might have changed the value. */
1357 if (!(zone && *zone) && tp->tm_isdst >= 0)
1358 zone = tzname[tp->tm_isdst];
1359#endif
1360 if (! zone)
1361 zone = "";
1362
1363#ifdef COMPILE_WIDE
1364 {
1365 /* The zone string is always given in multibyte form. We have
1366 to transform it first. */
1367 wchar_t *wczone;
1368 size_t len;
1369 widen (zone, wczone, len);
1370 cpy (len, wczone);
1371 }
1372#else
1373 cpy (strlen (zone), zone);
1374#endif
1375 break;
1376
1377 case L_('z'):
1378 if (tp->tm_isdst < 0)
1379 break;
1380
1381 {
1382 int diff;
1383#if HAVE_TM_GMTOFF
1384 diff = tp->tm_gmtoff;
1385#else
1386 if (ut)
1387 diff = 0;
1388 else
1389 {
1390 struct tm gtm;
1391 struct tm ltm;
1392 time_t lt;
1393
1394 ltm = *tp;
1395 lt = mktime (&ltm);
1396
1397 if (lt == (time_t) -1)
1398 {
1399 /* mktime returns -1 for errors, but -1 is also a
1400 valid time_t value. Check whether an error really
1401 occurred. */
1402 struct tm tm;
1403
1404 if (! my_strftime_localtime_r (&lt, &tm)
1405 || ((ltm.tm_sec ^ tm.tm_sec)
1406 | (ltm.tm_min ^ tm.tm_min)
1407 | (ltm.tm_hour ^ tm.tm_hour)
1408 | (ltm.tm_mday ^ tm.tm_mday)
1409 | (ltm.tm_mon ^ tm.tm_mon)
1410 | (ltm.tm_year ^ tm.tm_year)))
1411 break;
1412 }
1413
1414 if (! my_strftime_gmtime_r (&lt, &gtm))
1415 break;
1416
1417 diff = tm_diff (&ltm, &gtm);
1418 }
1419#endif
1420
1421 if (diff < 0)
1422 {
1423 add (1, *p = L_('-'));
1424 diff = -diff;
1425 }
1426 else
1427 add (1, *p = L_('+'));
1428
1429 diff /= 60;
1430 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1431 }
1432
1433 case L_('\0'): /* GNU extension: % at end of format. */
1434 --f;
1435 /* Fall through. */
1436 default:
1437 /* Unknown format; output the format, including the '%',
1438 since this is most likely the right thing to do if a
1439 multibyte string has been misparsed. */
1440 bad_format:
1441 {
1442 int flen;
1443 for (flen = 1; f[1 - flen] != L_('%'); flen++)
1444 continue;
1445 cpy (flen, &f[1 - flen]);
1446 }
1447 break;
1448 }
1449 }
1450
1451 if (p && maxsize != 0)
1452 *p = L_('\0');
1453 return i;
1454}
1455#ifdef _LIBC
1456libc_hidden_def (my_strftime)
1457#endif
1458
1459
1460#ifdef emacs
1461/* For Emacs we have a separate interface which corresponds to the normal
1462 strftime function and does not have the extra information whether the
1463 TP arguments comes from a `gmtime' call or not. */
1464size_t
1465emacs_strftime (s, maxsize, format, tp)
1466 char *s;
1467 size_t maxsize;
1468 const char *format;
1469 const struct tm *tp;
1470{
1471 return my_strftime (s, maxsize, format, tp, 0);
1472}
1473#endif
Note: See TracBrowser for help on using the repository browser.