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