source: vendor/diffutils/2.8.1/lib/strftime.c

Last change on this file was 2556, checked in by bird, 19 years ago

diffutils 2.8.1

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