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