]> sourceware.org Git - glibc.git/blame - time/strftime.c
Update.
[glibc.git] / time / strftime.c
CommitLineData
8d57beea 1/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
26761c28 2 This file is part of the GNU C Library.
28f540f4 3
26761c28
UD
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
28f540f4 8
26761c28
UD
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
28f540f4 13
26761c28
UD
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
28f540f4 18
0793d348
RM
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#ifdef _LIBC
24# define HAVE_LIMITS_H 1
25# define HAVE_MBLEN 1
ec4b0518 26# define HAVE_MBRLEN 1
d68171ed 27# define HAVE_STRUCT_ERA_ENTRY 1
f0f1bf85 28# define HAVE_TM_GMTOFF 1
0793d348 29# define HAVE_TM_ZONE 1
68dbb3a6
UD
30# define HAVE_TZNAME 1
31# define HAVE_TZSET 1
ec4b0518 32# define MULTIBYTE_IS_FORMAT_SAFE 1
0793d348 33# define STDC_HEADERS 1
0793d348
RM
34# include "../locale/localeinfo.h"
35#endif
36
a5a0310d
UD
37#if defined emacs && !defined HAVE_BCOPY
38# define HAVE_MEMCPY 1
39#endif
40
f8b87ef0 41#include <ctype.h>
0793d348 42#include <sys/types.h> /* Some systems define `time_t' here. */
75cd5204
RM
43
44#ifdef TIME_WITH_SYS_TIME
45# include <sys/time.h>
46# include <time.h>
47#else
48# ifdef HAVE_SYS_TIME_H
49# include <sys/time.h>
50# else
51# include <time.h>
52# endif
53#endif
8a4b65b4
UD
54#if HAVE_TZNAME
55extern char *tzname[];
56#endif
28f540f4 57
ec4b0518
UD
58/* Do multibyte processing if multibytes are supported, unless
59 multibyte sequences are safe in formats. Multibyte sequences are
60 safe if they cannot contain byte sequences that look like format
61 conversion specifications. The GNU C Library uses UTF8 multibyte
62 encoding, which is safe for formats, but strftime.c can be used
63 with other C libraries that use unsafe encodings. */
64#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
65
66#if DO_MULTIBYTE
67# if HAVE_MBRLEN
68# include <wchar.h>
69# else
70 /* Simulate mbrlen with mblen as best we can. */
71# define mbstate_t int
72# define mbrlen(s, n, ps) mblen (s, n)
73# define mbsinit(ps) (*(ps) == 0)
74# endif
75 static const mbstate_t mbstate_zero;
0793d348
RM
76#endif
77
78#if HAVE_LIMITS_H
79# include <limits.h>
28f540f4
RM
80#endif
81
0793d348
RM
82#if STDC_HEADERS
83# include <stddef.h>
84# include <stdlib.h>
85# include <string.h>
86#else
ae1025be
UD
87# ifndef HAVE_MEMCPY
88# define memcpy(d, s, n) bcopy ((s), (d), (n))
89# endif
0793d348
RM
90#endif
91
86187531
UD
92#ifdef _LIBC
93# define MEMPCPY(d, s, n) __mempcpy (d, s, n)
94#else
95# ifndef HAVE_MEMPCPY
96# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
97# endif
98#endif
99
0793d348 100#ifndef __P
6d52618b
UD
101# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
102# define __P(args) args
103# else
104# define __P(args) ()
105# endif /* GCC. */
0793d348
RM
106#endif /* Not __P. */
107
108#ifndef PTR
6d52618b
UD
109# ifdef __STDC__
110# define PTR void *
111# else
112# define PTR char *
113# endif
0793d348
RM
114#endif
115
f0f1bf85 116#ifndef CHAR_BIT
6d52618b
UD
117# define CHAR_BIT 8
118#endif
119
120#ifndef NULL
121# define NULL 0
f0f1bf85
UD
122#endif
123
124#define TYPE_SIGNED(t) ((t) -1 < 0)
125
126/* Bound on length of the string representing an integer value of type t.
127 Subtract one for the sign bit if t is signed;
128 302 / 1000 is log10 (2) rounded up;
129 add one for integer division truncation;
130 add one more for a minus sign if t is signed. */
131#define INT_STRLEN_BOUND(t) \
132 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
133
134#define TM_YEAR_BASE 1900
135
ec4b0518
UD
136#ifndef __isleap
137/* Nonzero if YEAR is a leap year (every 4 years,
138 except every 100th isn't, and every 400th is). */
6d52618b 139# define __isleap(year) \
ec4b0518
UD
140 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
141#endif
142
2de99474 143
fafaa44e
UD
144#ifdef _LIBC
145# define gmtime_r __gmtime_r
146# define localtime_r __localtime_r
10dc2a90
UD
147# define tzname __tzname
148# define tzset __tzset
fafaa44e
UD
149#else
150# if ! HAVE_LOCALTIME_R
151# if ! HAVE_TM_GMTOFF
152/* Approximate gmtime_r as best we can in its absence. */
a5a0310d 153# undef gmtime_r
76b87c03 154# define gmtime_r my_gmtime_r
fafaa44e
UD
155static struct tm *gmtime_r __P ((const time_t *, struct tm *));
156static struct tm *
157gmtime_r (t, tp)
158 const time_t *t;
159 struct tm *tp;
160{
161 struct tm *l = gmtime (t);
162 if (! l)
163 return 0;
164 *tp = *l;
165 return tp;
166}
167# endif /* ! HAVE_TM_GMTOFF */
168
169/* Approximate localtime_r as best we can in its absence. */
a5a0310d 170# undef localtime_r
9c2322bc 171# define localtime_r my_ftime_localtime_r
fafaa44e
UD
172static struct tm *localtime_r __P ((const time_t *, struct tm *));
173static struct tm *
174localtime_r (t, tp)
175 const time_t *t;
176 struct tm *tp;
177{
178 struct tm *l = localtime (t);
179 if (! l)
180 return 0;
181 *tp = *l;
182 return tp;
183}
184# endif /* ! HAVE_LOCALTIME_R */
185#endif /* ! defined (_LIBC) */
186
187
2d7da676 188#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
68dbb3a6
UD
189/* Some systems lack the `memset' function and we don't want to
190 introduce additional dependencies. */
2d7da676
UD
191/* The SGI compiler reportedly barfs on the trailing null
192 if we use a string constant as the initializer. 28 June 1997, rms. */
193static const char spaces[16] = /* " " */
194 { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
195static const char zeroes[16] = /* "0000000000000000" */
196 { '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0' };
68dbb3a6
UD
197
198# define memset_space(P, Len) \
199 do { \
200 int _len = (Len); \
63551311 201 \
68dbb3a6
UD
202 do \
203 { \
204 int _this = _len > 16 ? 16 : _len; \
86187531 205 (P) = mempcpy ((P), spaces, _this); \
68dbb3a6
UD
206 _len -= _this; \
207 } \
208 while (_len > 0); \
209 } while (0)
779ae82e
UD
210
211# define memset_zero(P, Len) \
212 do { \
213 int _len = (Len); \
214 \
215 do \
216 { \
217 int _this = _len > 16 ? 16 : _len; \
86187531 218 (P) = mempcpy ((P), zeroes, _this); \
779ae82e
UD
219 _len -= _this; \
220 } \
221 while (_len > 0); \
222 } while (0)
68dbb3a6 223#else
63551311 224# define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
779ae82e 225# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
68dbb3a6
UD
226#endif
227
76b87c03 228#define add(n, f) \
28f540f4
RM
229 do \
230 { \
f8b87ef0
UD
231 int _n = (n); \
232 int _delta = width - _n; \
6d52618b
UD
233 int _incr = _n + (_delta > 0 ? _delta : 0); \
234 if (i + _incr >= maxsize) \
28f540f4 235 return 0; \
6d52618b
UD
236 if (p) \
237 { \
238 if (_delta > 0) \
779ae82e
UD
239 { \
240 if (pad == '0') \
241 memset_zero (p, _delta); \
242 else \
243 memset_space (p, _delta); \
244 } \
6d52618b
UD
245 f; \
246 p += _n; \
247 } \
248 i += _incr; \
28f540f4 249 } while (0)
f8b87ef0 250
76b87c03 251#define cpy(n, s) \
f8b87ef0
UD
252 add ((n), \
253 if (to_lowcase) \
254 memcpy_lowcase (p, (s), _n); \
6d52618b
UD
255 else if (to_uppcase) \
256 memcpy_uppcase (p, (s), _n); \
f8b87ef0
UD
257 else \
258 memcpy ((PTR) p, (PTR) (s), _n))
259
260
261
262#ifdef _LIBC
263# define TOUPPER(Ch) toupper (Ch)
264# define TOLOWER(Ch) tolower (Ch)
265#else
266# define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
267# define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
268#endif
7e3be507
UD
269/* We don't use `isdigit' here since the locale dependent
270 interpretation is not what we want here. We only need to accept
271 the arabic digits in the ASCII range. One day there is perhaps a
272 more reliable way to accept other sets of digits. */
273#define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
f8b87ef0
UD
274
275static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
276
277static char *
278memcpy_lowcase (dest, src, len)
279 char *dest;
280 const char *src;
281 size_t len;
282{
283 while (len-- > 0)
1618c590 284 dest[len] = TOLOWER ((unsigned char) src[len]);
f8b87ef0
UD
285 return dest;
286}
0793d348 287
6d52618b
UD
288static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
289
290static char *
291memcpy_uppcase (dest, src, len)
292 char *dest;
293 const char *src;
294 size_t len;
295{
296 while (len-- > 0)
1618c590 297 dest[len] = TOUPPER ((unsigned char) src[len]);
6d52618b
UD
298 return dest;
299}
300
cf29ffbe 301
f0f1bf85
UD
302#if ! HAVE_TM_GMTOFF
303/* Yield the difference between *A and *B,
304 measured in seconds, ignoring leap seconds. */
9c2322bc 305# define tm_diff ftime_tm_diff
f0f1bf85
UD
306static int tm_diff __P ((const struct tm *, const struct tm *));
307static int
308tm_diff (a, b)
309 const struct tm *a;
310 const struct tm *b;
311{
ec4b0518
UD
312 /* Compute intervening leap days correctly even if year is negative.
313 Take care to avoid int overflow in leap day calculations,
314 but it's OK to assume that A and B are close to each other. */
315 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
316 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
317 int a100 = a4 / 25 - (a4 % 25 < 0);
318 int b100 = b4 / 25 - (b4 % 25 < 0);
319 int a400 = a100 >> 2;
320 int b400 = b100 >> 2;
321 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
322 int years = a->tm_year - b->tm_year;
f0f1bf85
UD
323 int days = (365 * years + intervening_leap_days
324 + (a->tm_yday - b->tm_yday));
325 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
326 + (a->tm_min - b->tm_min))
327 + (a->tm_sec - b->tm_sec));
328}
329#endif /* ! HAVE_TM_GMTOFF */
28f540f4 330
b7459e56
RM
331
332
ec4b0518
UD
333/* The number of days from the first day of the first ISO week of this
334 year to the year day YDAY with week day WDAY. ISO weeks start on
335 Monday; the first ISO week has the year's first Thursday. YDAY may
336 be as small as YDAY_MINIMUM. */
337#define ISO_WEEK_START_WDAY 1 /* Monday */
338#define ISO_WEEK1_WDAY 4 /* Thursday */
339#define YDAY_MINIMUM (-366)
340static int iso_week_days __P ((int, int));
341#ifdef __GNUC__
a5a0310d 342__inline__
28f540f4 343#endif
ec4b0518
UD
344static int
345iso_week_days (yday, wday)
346 int yday;
347 int wday;
28f540f4 348{
ec4b0518
UD
349 /* Add enough to the first operand of % to make it nonnegative. */
350 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
351 return (yday
352 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
353 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
28f540f4
RM
354}
355
ec4b0518 356
1618c590 357#if !(defined _NL_CURRENT || HAVE_STRFTIME)
0793d348
RM
358static char const weekday_name[][10] =
359 {
360 "Sunday", "Monday", "Tuesday", "Wednesday",
361 "Thursday", "Friday", "Saturday"
362 };
363static char const month_name[][10] =
364 {
365 "January", "February", "March", "April", "May", "June",
366 "July", "August", "September", "October", "November", "December"
367 };
368#endif
28f540f4 369
8d57beea 370
1618c590
UD
371#ifdef emacs
372# define my_strftime emacs_strftime
373 /* Emacs 20.2 uses `-Dstrftime=emacs_strftime' when compiling,
374 because that's how strftime used to be configured.
375 Undo this, since it gets in the way of accessing the underlying strftime,
376 which is needed for things like %Ec in Solaris.
377 The following two lines can be removed once Emacs stops compiling with
378 `-Dstrftime=emacs_strftime'. */
379# undef strftime
380size_t strftime __P ((char *, size_t, const char *, const struct tm *));
381#else
382# define my_strftime strftime
383#endif
384
8d57beea
UD
385#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
386 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
387 Work around this bug by copying *tp before it might be munged. */
388 size_t _strftime_copytm __P ((char *, size_t, const char *,
389 const struct tm *));
390 size_t
1618c590 391 my_strftime (s, maxsize, format, tp)
8d57beea
UD
392 char *s;
393 size_t maxsize;
394 const char *format;
395 const struct tm *tp;
396 {
397 struct tm tmcopy;
398 tmcopy = *tp;
399 return _strftime_copytm (s, maxsize, format, &tmcopy);
400 }
1618c590
UD
401# undef my_strftime
402# define my_strftime(S, Maxsize, Format, Tp) \
c0e45674 403 _strftime_copytm (S, Maxsize, Format, Tp)
8d57beea
UD
404#endif
405
406
28f540f4
RM
407/* Write information from TP into S according to the format
408 string FORMAT, writing no more that MAXSIZE characters
409 (including the terminating '\0') and returning number of
410 characters written. If S is NULL, nothing will be written
411 anywhere, so to determine how many characters would be
412 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
413size_t
1618c590 414my_strftime (s, maxsize, format, tp)
0793d348
RM
415 char *s;
416 size_t maxsize;
417 const char *format;
f8b87ef0 418 const struct tm *tp;
28f540f4 419{
0793d348
RM
420 int hour12 = tp->tm_hour;
421#ifdef _NL_CURRENT
422 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
423 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
424 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
425 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
426 const char *const ampm = _NL_CURRENT (LC_TIME,
b20e47cb 427 hour12 > 11 ? PM_STR : AM_STR);
d68171ed
UD
428 size_t aw_len = strlen (a_wkday);
429 size_t am_len = strlen (a_month);
0793d348
RM
430 size_t ap_len = strlen (ampm);
431#else
1618c590 432# if !HAVE_STRFTIME
0793d348
RM
433 const char *const f_wkday = weekday_name[tp->tm_wday];
434 const char *const f_month = month_name[tp->tm_mon];
435 const char *const a_wkday = f_wkday;
436 const char *const a_month = f_month;
b20e47cb 437 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
0793d348
RM
438 size_t aw_len = 3;
439 size_t am_len = 3;
440 size_t ap_len = 2;
1618c590 441# endif
0793d348 442#endif
1618c590 443#if defined _NL_CURRENT || !HAVE_STRFTIME
2de99474
UD
444 size_t wkday_len = strlen (f_wkday);
445 size_t month_len = strlen (f_month);
1618c590 446#endif
0793d348 447 const char *zone;
28f540f4 448 size_t zonelen;
f8b87ef0
UD
449 size_t i = 0;
450 char *p = s;
451 const char *f;
28f540f4 452
68dbb3a6 453 zone = NULL;
cd6ede75
UD
454#if HAVE_TM_ZONE
455 /* The POSIX test suite assumes that setting
68dbb3a6
UD
456 the environment variable TZ to a new value before calling strftime()
457 will influence the result (the %Z format) even if the information in
cd6ede75
UD
458 TP is computed with a totally different time zone.
459 This is bogus: though POSIX allows bad behavior like this,
460 POSIX does not require it. Do the right thing instead. */
0793d348
RM
461 zone = (const char *) tp->tm_zone;
462#endif
463#if HAVE_TZNAME
68dbb3a6
UD
464 /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
465 time zone names contained in the external variable `tzname' shall
466 be set as if the tzset() function had been called. */
467# if HAVE_TZSET
468 tzset ();
469# endif
470
0793d348
RM
471 if (!(zone && *zone) && tp->tm_isdst >= 0)
472 zone = tzname[tp->tm_isdst];
473#endif
ec4b0518
UD
474 if (! zone)
475 zone = ""; /* POSIX.2 requires the empty string here. */
0793d348
RM
476
477 zonelen = strlen (zone);
28f540f4
RM
478
479 if (hour12 > 12)
480 hour12 -= 12;
481 else
482 if (hour12 == 0) hour12 = 12;
483
484 for (f = format; *f != '\0'; ++f)
485 {
ec4b0518
UD
486 int pad; /* Padding for number ('-', '_', or 0). */
487 int modifier; /* Field modifier ('E', 'O', or 0). */
488 int digits; /* Max digits for numeric format. */
489 int number_value; /* Numeric value to be printed. */
f0f1bf85 490 int negative_number; /* 1 if the number is negative. */
ec4b0518 491 const char *subfmt;
f0f1bf85
UD
492 char *bufp;
493 char buf[1 + (sizeof (int) < sizeof (time_t)
494 ? INT_STRLEN_BOUND (time_t)
495 : INT_STRLEN_BOUND (int))];
f8b87ef0
UD
496 int width = -1;
497 int to_lowcase = 0;
6d52618b 498 int to_uppcase = 0;
cf29ffbe 499 int change_case = 0;
1618c590 500 int format_char;
28f540f4 501
ec4b0518
UD
502#if DO_MULTIBYTE
503
504 switch (*f)
28f540f4 505 {
ec4b0518
UD
506 case '%':
507 break;
508
509 case '\a': case '\b': case '\t': case '\n':
510 case '\v': case '\f': case '\r':
511 case ' ': case '!': case '"': case '#': case '&': case'\'':
512 case '(': case ')': case '*': case '+': case ',': case '-':
513 case '.': case '/': case '0': case '1': case '2': case '3':
514 case '4': case '5': case '6': case '7': case '8': case '9':
515 case ':': case ';': case '<': case '=': case '>': case '?':
516 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
517 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
518 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
519 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
520 case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
521 case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
522 case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
523 case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
524 case 'r': case 's': case 't': case 'u': case 'v': case 'w':
525 case 'x': case 'y': case 'z': case '{': case '|': case '}':
526 case '~':
527 /* The C Standard requires these 98 characters (plus '%') to
528 be in the basic execution character set. None of these
529 characters can start a multibyte sequence, so they need
530 not be analyzed further. */
531 add (1, *p = *f);
532 continue;
533
534 default:
535 /* Copy this multibyte sequence until we reach its end, find
536 an error, or come back to the initial shift state. */
537 {
538 mbstate_t mbstate = mbstate_zero;
539 size_t len = 0;
540
541 do
542 {
543 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
544
545 if (bytes == 0)
546 break;
547
548 if (bytes == (size_t) -2 || bytes == (size_t) -1)
549 {
550 len++;
551 break;
552 }
553
554 len += bytes;
555 }
556 while (! mbsinit (&mbstate));
557
558 cpy (len, f);
559 continue;
560 }
28f540f4
RM
561 }
562
ec4b0518
UD
563#else /* ! DO_MULTIBYTE */
564
565 /* Either multibyte encodings are not supported, or they are
566 safe for formats, so any non-'%' byte can be copied through. */
28f540f4
RM
567 if (*f != '%')
568 {
2de99474 569 add (1, *p = *f);
28f540f4
RM
570 continue;
571 }
572
ec4b0518
UD
573#endif /* ! DO_MULTIBYTE */
574
7e3be507 575 /* Check for flags that can modify a format. */
01c901a5 576 pad = 0;
f8b87ef0 577 while (1)
b7459e56 578 {
6d52618b 579 switch (*++f)
f8b87ef0 580 {
7e3be507 581 /* This influences the number formats. */
f8b87ef0
UD
582 case '_':
583 case '-':
584 case '0':
6d52618b
UD
585 pad = *f;
586 continue;
587
7e3be507 588 /* This changes textual output. */
6d52618b
UD
589 case '^':
590 to_uppcase = 1;
591 continue;
cf29ffbe
UD
592 case '#':
593 change_case = 1;
594 continue;
f8b87ef0
UD
595
596 default:
f8b87ef0
UD
597 break;
598 }
b7459e56 599 break;
f8b87ef0 600 }
ec4b0518 601
f8b87ef0 602 /* As a GNU extension we allow to specify the field width. */
7e3be507 603 if (ISDIGIT (*f))
f8b87ef0
UD
604 {
605 width = 0;
606 do
607 {
608 width *= 10;
609 width += *f - '0';
7e3be507 610 ++f;
f8b87ef0 611 }
7e3be507 612 while (ISDIGIT (*f));
b7459e56
RM
613 }
614
f0f1bf85
UD
615 /* Check for modifiers. */
616 switch (*f)
617 {
618 case 'E':
f0f1bf85 619 case 'O':
ec4b0518 620 modifier = *f++;
f0f1bf85 621 break;
ec4b0518 622
f0f1bf85 623 default:
ec4b0518 624 modifier = 0;
f0f1bf85
UD
625 break;
626 }
627
b7459e56 628 /* Now do the specified format. */
1618c590
UD
629 format_char = *f;
630 switch (format_char)
28f540f4 631 {
f0f1bf85 632#define DO_NUMBER(d, v) \
cf29ffbe
UD
633 digits = width == -1 ? d : width; \
634 number_value = v; goto do_number
f0f1bf85 635#define DO_NUMBER_SPACEPAD(d, v) \
cf29ffbe
UD
636 digits = width == -1 ? d : width; \
637 number_value = v; goto do_number_spacepad
f0f1bf85 638
28f540f4 639 case '%':
ec4b0518 640 if (modifier != 0)
f0f1bf85 641 goto bad_format;
2de99474 642 add (1, *p = *f);
28f540f4
RM
643 break;
644
645 case 'a':
ec4b0518 646 if (modifier != 0)
f0f1bf85 647 goto bad_format;
cf29ffbe
UD
648 if (change_case)
649 {
650 to_uppcase = 1;
651 to_lowcase = 0;
652 }
1618c590 653#if defined _NL_CURRENT || !HAVE_STRFTIME
2de99474 654 cpy (aw_len, a_wkday);
28f540f4 655 break;
1618c590
UD
656#else
657 goto underlying_strftime;
658#endif
28f540f4
RM
659
660 case 'A':
ec4b0518 661 if (modifier != 0)
f0f1bf85 662 goto bad_format;
cf29ffbe
UD
663 if (change_case)
664 {
665 to_uppcase = 1;
666 to_lowcase = 0;
667 }
1618c590 668#if defined _NL_CURRENT || !HAVE_STRFTIME
2de99474 669 cpy (wkday_len, f_wkday);
28f540f4 670 break;
1618c590
UD
671#else
672 goto underlying_strftime;
673#endif
28f540f4
RM
674
675 case 'b':
ec4b0518
UD
676 case 'h': /* POSIX.2 extension. */
677 if (modifier != 0)
f0f1bf85 678 goto bad_format;
1618c590 679#if defined _NL_CURRENT || !HAVE_STRFTIME
2de99474 680 cpy (am_len, a_month);
28f540f4 681 break;
1618c590
UD
682#else
683 goto underlying_strftime;
684#endif
28f540f4
RM
685
686 case 'B':
ec4b0518 687 if (modifier != 0)
f0f1bf85 688 goto bad_format;
cf29ffbe
UD
689 if (change_case)
690 {
691 to_uppcase = 1;
692 to_lowcase = 0;
693 }
1618c590 694#if defined _NL_CURRENT || !HAVE_STRFTIME
2de99474 695 cpy (month_len, f_month);
28f540f4 696 break;
1618c590
UD
697#else
698 goto underlying_strftime;
699#endif
28f540f4
RM
700
701 case 'c':
ec4b0518 702 if (modifier == 'O')
f0f1bf85 703 goto bad_format;
0793d348 704#ifdef _NL_CURRENT
ec4b0518
UD
705 if (! (modifier == 'E'
706 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
f0f1bf85 707 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
0793d348 708#else
1618c590
UD
709# if HAVE_STRFTIME
710 goto underlying_strftime;
711# else
f65fd747 712 subfmt = "%a %b %e %H:%M:%S %Y";
1618c590 713# endif
0793d348 714#endif
f0f1bf85 715
28f540f4
RM
716 subformat:
717 {
6d52618b 718 char *old_start = p;
1618c590 719 size_t len = my_strftime (NULL, maxsize - i, subfmt, tp);
0793d348
RM
720 if (len == 0 && *subfmt)
721 return 0;
1618c590 722 add (len, my_strftime (p, maxsize - i, subfmt, tp));
6d52618b
UD
723
724 if (to_uppcase)
725 while (old_start < p)
726 {
1618c590 727 *old_start = TOUPPER ((unsigned char) *old_start);
6d52618b
UD
728 ++old_start;
729 }
28f540f4
RM
730 }
731 break;
732
1618c590
UD
733#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
734 underlying_strftime:
735 {
736 /* The relevant information is available only via the
737 underlying strftime implementation, so use that. */
738 char ufmt[4];
739 char *u = ufmt;
740 char ubuf[1024]; /* enough for any single format in practice */
741 size_t len;
742 *u++ = '%';
743 if (modifier != 0)
744 *u++ = modifier;
745 *u++ = format_char;
746 *u = '\0';
747 len = strftime (ubuf, sizeof ubuf, ufmt, tp);
748 if (len == 0)
749 return 0;
750 cpy (len, ubuf);
751 }
752 break;
753#endif
754
ec4b0518
UD
755 case 'C': /* POSIX.2 extension. */
756 if (modifier == 'O')
f0f1bf85 757 goto bad_format;
d68171ed
UD
758 if (modifier == 'E')
759 {
1618c590 760#if HAVE_STRUCT_ERA_ENTRY
d68171ed
UD
761 struct era_entry *era = _nl_get_era_entry (tp);
762 if (era)
763 {
764 size_t len = strlen (era->name_fmt);
765 cpy (len, era->name_fmt);
766 break;
767 }
1618c590
UD
768#else
769# if HAVE_STRFTIME
770 goto underlying_strftime;
771# endif
f0f1bf85 772#endif
1618c590
UD
773 }
774
ec4b0518
UD
775 {
776 int year = tp->tm_year + TM_YEAR_BASE;
777 DO_NUMBER (1, year / 100 - (year % 100 < 0));
778 }
28f540f4 779
0793d348 780 case 'x':
ec4b0518 781 if (modifier == 'O')
f0f1bf85 782 goto bad_format;
0793d348 783#ifdef _NL_CURRENT
ec4b0518
UD
784 if (! (modifier == 'E'
785 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
f0f1bf85 786 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
0793d348 787 goto subformat;
1618c590
UD
788#else
789# if HAVE_STRFTIME
790 goto underlying_strftime;
791# else
0793d348 792 /* Fall through. */
1618c590
UD
793# endif
794#endif
ec4b0518
UD
795 case 'D': /* POSIX.2 extension. */
796 if (modifier != 0)
797 goto bad_format;
28f540f4
RM
798 subfmt = "%m/%d/%y";
799 goto subformat;
800
801 case 'd':
ec4b0518 802 if (modifier == 'E')
f0f1bf85
UD
803 goto bad_format;
804
b7459e56 805 DO_NUMBER (2, tp->tm_mday);
28f540f4 806
ec4b0518
UD
807 case 'e': /* POSIX.2 extension. */
808 if (modifier == 'E')
f0f1bf85
UD
809 goto bad_format;
810
2064087b 811 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
b7459e56 812
f0f1bf85 813 /* All numeric formats set DIGITS and NUMBER_VALUE and then
b7459e56
RM
814 jump to one of these two labels. */
815
2064087b 816 do_number_spacepad:
f8b87ef0
UD
817 /* Force `_' flag unless overwritten by `0' flag. */
818 if (pad != '0')
819 pad = '_';
b7459e56
RM
820
821 do_number:
f0f1bf85
UD
822 /* Format the number according to the MODIFIER flag. */
823
ec4b0518 824 if (modifier == 'O' && 0 <= number_value)
f0f1bf85 825 {
1618c590 826#ifdef _NL_CURRENT
c4029823
UD
827 /* Get the locale specific alternate representation of
828 the number NUMBER_VALUE. If none exist NULL is returned. */
829 const char *cp = _nl_get_alt_digit (number_value);
ec4b0518 830
c4029823 831 if (cp != NULL)
ec4b0518
UD
832 {
833 size_t digitlen = strlen (cp);
834 if (digitlen != 0)
835 {
836 cpy (digitlen, cp);
837 break;
838 }
839 }
1618c590
UD
840#else
841# if HAVE_STRFTIME
842 goto underlying_strftime;
843# endif
f0f1bf85 844#endif
1618c590 845 }
b7459e56 846 {
f0f1bf85 847 unsigned int u = number_value;
b7459e56 848
f0f1bf85
UD
849 bufp = buf + sizeof (buf);
850 negative_number = number_value < 0;
b7459e56 851
f0f1bf85
UD
852 if (negative_number)
853 u = -u;
b7459e56 854
f0f1bf85
UD
855 do
856 *--bufp = u % 10 + '0';
857 while ((u /= 10) != 0);
858 }
b7459e56 859
f0f1bf85
UD
860 do_number_sign_and_padding:
861 if (negative_number)
862 *--bufp = '-';
863
ec4b0518 864 if (pad != '-')
f0f1bf85
UD
865 {
866 int padding = digits - (buf + sizeof (buf) - bufp);
867
ec4b0518 868 if (pad == '_')
f0f1bf85
UD
869 {
870 while (0 < padding--)
871 *--bufp = ' ';
872 }
873 else
874 {
875 bufp += negative_number;
876 while (0 < padding--)
877 *--bufp = '0';
878 if (negative_number)
879 *--bufp = '-';
880 }
881 }
882
883 cpy (buf + sizeof (buf) - bufp, bufp);
f0f1bf85 884 break;
b7459e56 885
28f540f4
RM
886
887 case 'H':
ec4b0518 888 if (modifier == 'E')
f0f1bf85
UD
889 goto bad_format;
890
b7459e56 891 DO_NUMBER (2, tp->tm_hour);
28f540f4
RM
892
893 case 'I':
ec4b0518 894 if (modifier == 'E')
f0f1bf85
UD
895 goto bad_format;
896
b7459e56 897 DO_NUMBER (2, hour12);
28f540f4
RM
898
899 case 'k': /* GNU extension. */
ec4b0518 900 if (modifier == 'E')
f0f1bf85
UD
901 goto bad_format;
902
2064087b 903 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
28f540f4
RM
904
905 case 'l': /* GNU extension. */
ec4b0518 906 if (modifier == 'E')
f0f1bf85
UD
907 goto bad_format;
908
2064087b 909 DO_NUMBER_SPACEPAD (2, hour12);
28f540f4
RM
910
911 case 'j':
ec4b0518 912 if (modifier == 'E')
f0f1bf85
UD
913 goto bad_format;
914
b7459e56 915 DO_NUMBER (3, 1 + tp->tm_yday);
28f540f4
RM
916
917 case 'M':
ec4b0518 918 if (modifier == 'E')
f0f1bf85
UD
919 goto bad_format;
920
b7459e56 921 DO_NUMBER (2, tp->tm_min);
28f540f4
RM
922
923 case 'm':
ec4b0518 924 if (modifier == 'E')
f0f1bf85
UD
925 goto bad_format;
926
b7459e56 927 DO_NUMBER (2, tp->tm_mon + 1);
28f540f4 928
ec4b0518 929 case 'n': /* POSIX.2 extension. */
28f540f4
RM
930 add (1, *p = '\n');
931 break;
932
f8b87ef0
UD
933 case 'P':
934 to_lowcase = 1;
1618c590
UD
935#if !defined _NL_CURRENT && HAVE_STRFTIME
936 format_char = 'p';
937#endif
f8b87ef0
UD
938 /* FALLTHROUGH */
939
28f540f4 940 case 'p':
cf29ffbe
UD
941 if (change_case)
942 {
943 to_uppcase = 0;
944 to_lowcase = 1;
945 }
1618c590 946#if defined _NL_CURRENT || !HAVE_STRFTIME
2de99474 947 cpy (ap_len, ampm);
28f540f4 948 break;
1618c590
UD
949#else
950 goto underlying_strftime;
951#endif
28f540f4
RM
952
953 case 'R': /* GNU extension. */
954 subfmt = "%H:%M";
955 goto subformat;
956
ec4b0518
UD
957 case 'r': /* POSIX.2 extension. */
958#ifdef _NL_CURRENT
959 if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
960#endif
961 subfmt = "%I:%M:%S %p";
28f540f4
RM
962 goto subformat;
963
964 case 'S':
ec4b0518
UD
965 if (modifier == 'E')
966 goto bad_format;
f0f1bf85 967
b7459e56 968 DO_NUMBER (2, tp->tm_sec);
28f540f4 969
2de99474 970 case 's': /* GNU extension. */
f0f1bf85 971 {
b33f91e9
UD
972 struct tm ltm;
973 time_t t;
974
975 ltm = *tp;
976 t = mktime (&ltm);
f0f1bf85
UD
977
978 /* Generate string value for T using time_t arithmetic;
979 this works even if sizeof (long) < sizeof (time_t). */
980
981 bufp = buf + sizeof (buf);
982 negative_number = t < 0;
983
984 do
985 {
986 int d = t % 10;
987 t /= 10;
988
989 if (negative_number)
990 {
991 d = -d;
992
993 /* Adjust if division truncates to minus infinity. */
994 if (0 < -1 % 10 && d < 0)
995 {
996 t++;
997 d += 10;
998 }
999 }
1000
1001 *--bufp = d + '0';
1002 }
1003 while (t != 0);
1004
1005 digits = 1;
1006 goto do_number_sign_and_padding;
2de99474 1007 }
2de99474 1008
0793d348 1009 case 'X':
ec4b0518 1010 if (modifier == 'O')
f0f1bf85 1011 goto bad_format;
0793d348 1012#ifdef _NL_CURRENT
ec4b0518
UD
1013 if (! (modifier == 'E'
1014 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
f0f1bf85 1015 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
0793d348 1016 goto subformat;
1618c590
UD
1017#else
1018# if HAVE_STRFTIME
1019 goto underlying_strftime;
1020# else
0793d348 1021 /* Fall through. */
1618c590
UD
1022# endif
1023#endif
ec4b0518 1024 case 'T': /* POSIX.2 extension. */
28f540f4
RM
1025 subfmt = "%H:%M:%S";
1026 goto subformat;
1027
ec4b0518 1028 case 't': /* POSIX.2 extension. */
28f540f4
RM
1029 add (1, *p = '\t');
1030 break;
1031
ec4b0518
UD
1032 case 'u': /* POSIX.2 extension. */
1033 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1034
28f540f4 1035 case 'U':
ec4b0518 1036 if (modifier == 'E')
f0f1bf85
UD
1037 goto bad_format;
1038
ec4b0518 1039 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
28f540f4 1040
339841f9 1041 case 'V':
ec4b0518
UD
1042 case 'g': /* GNU extension. */
1043 case 'G': /* GNU extension. */
1044 if (modifier == 'E')
f0f1bf85 1045 goto bad_format;
ec4b0518
UD
1046 {
1047 int year = tp->tm_year + TM_YEAR_BASE;
1048 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1049
1050 if (days < 0)
1051 {
1052 /* This ISO week belongs to the previous year. */
1053 year--;
1054 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
1055 tp->tm_wday);
1056 }
1057 else
1058 {
1059 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1060 tp->tm_wday);
1061 if (0 <= d)
1062 {
1063 /* This ISO week belongs to the next year. */
1064 year++;
1065 days = d;
1066 }
1067 }
1068
1069 switch (*f)
1070 {
1071 case 'g':
1072 DO_NUMBER (2, (year % 100 + 100) % 100);
1073
1074 case 'G':
1075 DO_NUMBER (1, year);
f0f1bf85 1076
ec4b0518
UD
1077 default:
1078 DO_NUMBER (2, days / 7 + 1);
1079 }
1080 }
339841f9 1081
28f540f4 1082 case 'W':
ec4b0518 1083 if (modifier == 'E')
f0f1bf85
UD
1084 goto bad_format;
1085
ec4b0518 1086 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
28f540f4
RM
1087
1088 case 'w':
ec4b0518 1089 if (modifier == 'E')
f0f1bf85
UD
1090 goto bad_format;
1091
ec4b0518 1092 DO_NUMBER (1, tp->tm_wday);
28f540f4 1093
28f540f4 1094 case 'Y':
d68171ed
UD
1095 if (modifier == 'E')
1096 {
1618c590 1097#if HAVE_STRUCT_ERA_ENTRY
d68171ed
UD
1098 struct era_entry *era = _nl_get_era_entry (tp);
1099 if (era)
1100 {
1101 subfmt = strchr (era->name_fmt, '\0') + 1;
1102 goto subformat;
1103 }
1618c590
UD
1104#else
1105# if HAVE_STRFTIME
1106 goto underlying_strftime;
1107# endif
f0f1bf85 1108#endif
1618c590 1109 }
ec4b0518
UD
1110 if (modifier == 'O')
1111 goto bad_format;
1112 else
1113 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
28f540f4
RM
1114
1115 case 'y':
d68171ed
UD
1116 if (modifier == 'E')
1117 {
1618c590 1118#if HAVE_STRUCT_ERA_ENTRY
d68171ed
UD
1119 struct era_entry *era = _nl_get_era_entry (tp);
1120 if (era)
1121 {
1122 int delta = tp->tm_year - era->start_date[0];
1123 DO_NUMBER (1, (era->offset
1124 + (era->direction == '-' ? -delta : delta)));
1125 }
1618c590
UD
1126#else
1127# if HAVE_STRFTIME
1128 goto underlying_strftime;
1129# endif
f0f1bf85 1130#endif
1618c590 1131 }
ec4b0518 1132 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
28f540f4
RM
1133
1134 case 'Z':
cf29ffbe
UD
1135 if (change_case)
1136 {
1137 to_uppcase = 0;
1138 to_lowcase = 1;
1139 }
26761c28 1140 cpy (zonelen, zone);
28f540f4
RM
1141 break;
1142
f0f1bf85
UD
1143 case 'z': /* GNU extension. */
1144 if (tp->tm_isdst < 0)
1145 break;
1146
2de99474 1147 {
2de99474 1148 int diff;
f0f1bf85
UD
1149#if HAVE_TM_GMTOFF
1150 diff = tp->tm_gmtoff;
1151#else
1152 struct tm gtm;
b33f91e9
UD
1153 struct tm ltm;
1154 time_t lt;
1155
1156 ltm = *tp;
1157 lt = mktime (&ltm);
2de99474 1158
f0f1bf85 1159 if (lt == (time_t) -1)
2de99474 1160 {
f0f1bf85
UD
1161 /* mktime returns -1 for errors, but -1 is also a
1162 valid time_t value. Check whether an error really
1163 occurred. */
1164 struct tm tm;
1165 localtime_r (&lt, &tm);
1166
1167 if ((ltm.tm_sec ^ tm.tm_sec)
1168 | (ltm.tm_min ^ tm.tm_min)
1169 | (ltm.tm_hour ^ tm.tm_hour)
1170 | (ltm.tm_mday ^ tm.tm_mday)
1171 | (ltm.tm_mon ^ tm.tm_mon)
1172 | (ltm.tm_year ^ tm.tm_year))
1173 break;
1174 }
dcf0671d 1175
f0f1bf85
UD
1176 if (! gmtime_r (&lt, &gtm))
1177 break;
dcf0671d 1178
f0f1bf85
UD
1179 diff = tm_diff (&ltm, &gtm);
1180#endif
2de99474
UD
1181
1182 if (diff < 0)
1183 {
1184 add (1, *p = '-');
1185 diff = -diff;
1186 }
1187 else
1188 add (1, *p = '+');
1189
f0f1bf85
UD
1190 diff /= 60;
1191 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
2de99474
UD
1192 }
1193
d68171ed
UD
1194 case '\0': /* GNU extension: % at end of format. */
1195 --f;
1196 /* Fall through. */
28f540f4 1197 default:
ec4b0518
UD
1198 /* Unknown format; output the format, including the '%',
1199 since this is most likely the right thing to do if a
1200 multibyte string has been misparsed. */
f0f1bf85 1201 bad_format:
ec4b0518
UD
1202 {
1203 int flen;
d68171ed 1204 for (flen = 1; f[1 - flen] != '%'; flen++)
ec4b0518
UD
1205 continue;
1206 cpy (flen, &f[1 - flen]);
1207 }
28f540f4
RM
1208 break;
1209 }
1210 }
1211
0793d348 1212 if (p)
28f540f4
RM
1213 *p = '\0';
1214 return i;
1215}
This page took 0.206348 seconds and 5 git commands to generate.