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