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