-/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+/* Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc.
-NOTE: The canonical source of this file is maintained with the GNU C Library.
-Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@gnu.org.
-This program is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
-later version.
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-USA. */
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
+/* Some hosts need this in order to declare localtime_r properly. */
+#ifndef _REENTRANT
+# define _REENTRANT 1
+#endif
+
#ifdef _LIBC
# define HAVE_LIMITS_H 1
# define HAVE_MBLEN 1
# define HAVE_TZSET 1
# define MULTIBYTE_IS_FORMAT_SAFE 1
# define STDC_HEADERS 1
-# include <ansidecl.h>
# include "../locale/localeinfo.h"
#endif
+#if defined emacs && !defined HAVE_BCOPY
+# define HAVE_MEMCPY 1
+#endif
+
#include <ctype.h>
#include <sys/types.h> /* Some systems define `time_t' here. */
# include <stdlib.h>
# include <string.h>
#else
-# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#ifdef _LIBC
+# define MEMPCPY(d, s, n) __mempcpy (d, s, n)
+#else
+# ifndef HAVE_MEMPCPY
+# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
+# endif
#endif
#ifndef __P
#ifdef _LIBC
# define gmtime_r __gmtime_r
# define localtime_r __localtime_r
-extern int __tz_compute __P ((time_t timer, const struct tm *tm));
# define tzname __tzname
# define tzset __tzset
#else
# if ! HAVE_LOCALTIME_R
# if ! HAVE_TM_GMTOFF
/* Approximate gmtime_r as best we can in its absence. */
-# define gmtime_r my_gmtime_r
+# undef gmtime_r
+# define gmtime_r my_gmtime_r
static struct tm *gmtime_r __P ((const time_t *, struct tm *));
static struct tm *
gmtime_r (t, tp)
# endif /* ! HAVE_TM_GMTOFF */
/* Approximate localtime_r as best we can in its absence. */
-# define localtime_r my_localtime_r
+# undef localtime_r
+# define localtime_r my_ftime_localtime_r
static struct tm *localtime_r __P ((const time_t *, struct tm *));
static struct tm *
localtime_r (t, tp)
return tp;
}
# endif /* ! HAVE_LOCALTIME_R */
-#endif /* ! defined (_LIBC) */
+#endif /* ! defined _LIBC */
-#if !defined (memset) && !defined (HAVE_MEMSET) && !defined (_LIBC)
+#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
/* Some systems lack the `memset' function and we don't want to
introduce additional dependencies. */
-static const char spaces[16] = " ";
-static const char zeroes[16] = "0000000000000000";
+/* The SGI compiler reportedly barfs on the trailing null
+ if we use a string constant as the initializer. 28 June 1997, rms. */
+static const char spaces[16] = /* " " */
+ { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
+static const char zeroes[16] = /* "0000000000000000" */
+ { '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0' };
# define memset_space(P, Len) \
do { \
do \
{ \
int _this = _len > 16 ? 16 : _len; \
- memcpy ((P), spaces, _this); \
- (P) += _this; \
+ (P) = MEMPCPY ((P), spaces, _this); \
_len -= _this; \
} \
while (_len > 0); \
do \
{ \
int _this = _len > 16 ? 16 : _len; \
- memcpy ((P), zeroes, _this); \
- (P) += _this; \
+ (P) = MEMPCPY ((P), zeroes, _this); \
_len -= _this; \
} \
while (_len > 0); \
# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
#endif
-#define add(n, f) \
+#define add(n, f) \
do \
{ \
int _n = (n); \
i += _incr; \
} while (0)
-#define cpy(n, s) \
+#define cpy(n, s) \
add ((n), \
if (to_lowcase) \
memcpy_lowcase (p, (s), _n); \
size_t len;
{
while (len-- > 0)
- dest[len] = TOLOWER (src[len]);
+ dest[len] = TOLOWER ((unsigned char) src[len]);
return dest;
}
size_t len;
{
while (len-- > 0)
- dest[len] = TOUPPER (src[len]);
+ dest[len] = TOUPPER ((unsigned char) src[len]);
return dest;
}
+
#if ! HAVE_TM_GMTOFF
/* Yield the difference between *A and *B,
measured in seconds, ignoring leap seconds. */
+# define tm_diff ftime_tm_diff
static int tm_diff __P ((const struct tm *, const struct tm *));
static int
tm_diff (a, b)
#define YDAY_MINIMUM (-366)
static int iso_week_days __P ((int, int));
#ifdef __GNUC__
-inline
+__inline__
#endif
static int
iso_week_days (yday, wday)
}
-#ifndef _NL_CURRENT
+#if !(defined _NL_CURRENT || HAVE_STRFTIME)
static char const weekday_name[][10] =
{
"Sunday", "Monday", "Tuesday", "Wednesday",
#endif
+#ifdef emacs
+# define my_strftime emacs_strftime
+#else
+# define my_strftime strftime
+#endif
+
#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
/* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
Work around this bug by copying *tp before it might be munged. */
size_t _strftime_copytm __P ((char *, size_t, const char *,
const struct tm *));
size_t
- strftime (s, maxsize, format, tp)
+ my_strftime (s, maxsize, format, tp)
char *s;
size_t maxsize;
const char *format;
tmcopy = *tp;
return _strftime_copytm (s, maxsize, format, &tmcopy);
}
-# ifdef strftime
-# undef strftime
-# endif
-# define strftime(S, Maxsize, Format, Tp) \
+# undef my_strftime
+# define my_strftime(S, Maxsize, Format, Tp) \
_strftime_copytm (S, Maxsize, Format, Tp)
#endif
anywhere, so to determine how many characters would be
written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
size_t
-strftime (s, maxsize, format, tp)
+my_strftime (s, maxsize, format, tp)
char *s;
size_t maxsize;
const char *format;
size_t am_len = strlen (a_month);
size_t ap_len = strlen (ampm);
#else
+# if !HAVE_STRFTIME
const char *const f_wkday = weekday_name[tp->tm_wday];
const char *const f_month = month_name[tp->tm_mon];
const char *const a_wkday = f_wkday;
size_t aw_len = 3;
size_t am_len = 3;
size_t ap_len = 2;
+# endif
#endif
+#if defined _NL_CURRENT || !HAVE_STRFTIME
size_t wkday_len = strlen (f_wkday);
size_t month_len = strlen (f_month);
+#endif
const char *zone;
size_t zonelen;
size_t i = 0;
const char *f;
zone = NULL;
-#if !defined _LIBC && HAVE_TM_ZONE
- /* XXX We have some problems here. First, the string pointed to by
- tm_zone is dynamically allocated while loading the zone data. But
- when another zone is loaded since the information in TP were
- computed this would be a stale pointer.
- The second problem is the POSIX test suite which assumes setting
+#if HAVE_TM_ZONE
+ /* The POSIX test suite assumes that setting
the environment variable TZ to a new value before calling strftime()
will influence the result (the %Z format) even if the information in
- TP is computed with a totally different time zone. --drepper@gnu */
+ TP is computed with a totally different time zone.
+ This is bogus: though POSIX allows bad behavior like this,
+ POSIX does not require it. Do the right thing instead. */
zone = (const char *) tp->tm_zone;
#endif
#if HAVE_TZNAME
for (f = format; *f != '\0'; ++f)
{
- int pad; /* Padding for number ('-', '_', or 0). */
+ int pad = 0; /* Padding for number ('-', '_', or 0). */
int modifier; /* Field modifier ('E', 'O', or 0). */
int digits; /* Max digits for numeric format. */
int number_value; /* Numeric value to be printed. */
int width = -1;
int to_lowcase = 0;
int to_uppcase = 0;
+ int change_case = 0;
+ int format_char;
#if DO_MULTIBYTE
#endif /* ! DO_MULTIBYTE */
/* Check for flags that can modify a format. */
- pad = 0;
while (1)
{
switch (*++f)
case '^':
to_uppcase = 1;
continue;
+ case '#':
+ change_case = 1;
+ continue;
default:
break;
}
/* Now do the specified format. */
- switch (*f)
+ format_char = *f;
+ switch (format_char)
{
#define DO_NUMBER(d, v) \
- digits = d; number_value = v; goto do_number
+ digits = width == -1 ? d : width; \
+ number_value = v; goto do_number
#define DO_NUMBER_SPACEPAD(d, v) \
- digits = d; number_value = v; goto do_number_spacepad
+ digits = width == -1 ? d : width; \
+ number_value = v; goto do_number_spacepad
case '%':
if (modifier != 0)
case 'a':
if (modifier != 0)
goto bad_format;
+ if (change_case)
+ {
+ to_uppcase = 1;
+ to_lowcase = 0;
+ }
+#if defined _NL_CURRENT || !HAVE_STRFTIME
cpy (aw_len, a_wkday);
break;
+#else
+ goto underlying_strftime;
+#endif
case 'A':
if (modifier != 0)
goto bad_format;
+ if (change_case)
+ {
+ to_uppcase = 1;
+ to_lowcase = 0;
+ }
+#if defined _NL_CURRENT || !HAVE_STRFTIME
cpy (wkday_len, f_wkday);
break;
+#else
+ goto underlying_strftime;
+#endif
case 'b':
case 'h': /* POSIX.2 extension. */
if (modifier != 0)
goto bad_format;
+#if defined _NL_CURRENT || !HAVE_STRFTIME
cpy (am_len, a_month);
break;
+#else
+ goto underlying_strftime;
+#endif
case 'B':
if (modifier != 0)
goto bad_format;
+ if (change_case)
+ {
+ to_uppcase = 1;
+ to_lowcase = 0;
+ }
+#if defined _NL_CURRENT || !HAVE_STRFTIME
cpy (month_len, f_month);
break;
+#else
+ goto underlying_strftime;
+#endif
case 'c':
if (modifier == 'O')
&& *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
#else
+# if HAVE_STRFTIME
+ goto underlying_strftime;
+# else
subfmt = "%a %b %e %H:%M:%S %Y";
+# endif
#endif
subformat:
{
char *old_start = p;
- size_t len = strftime (NULL, maxsize - i, subfmt, tp);
+ size_t len = my_strftime (NULL, maxsize - i, subfmt, tp);
if (len == 0 && *subfmt)
return 0;
- add (len, strftime (p, maxsize - i, subfmt, tp));
+ add (len, my_strftime (p, maxsize - i, subfmt, tp));
if (to_uppcase)
while (old_start < p)
{
- *old_start = TOUPPER (*old_start);
+ *old_start = TOUPPER ((unsigned char) *old_start);
++old_start;
}
}
break;
+#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
+ underlying_strftime:
+ {
+ /* The relevant information is available only via the
+ underlying strftime implementation, so use that. */
+ char ufmt[4];
+ char *u = ufmt;
+ char ubuf[1024]; /* enough for any single format in practice */
+ size_t len;
+ *u++ = '%';
+ if (modifier != 0)
+ *u++ = modifier;
+ *u++ = format_char;
+ *u = '\0';
+ len = strftime (ubuf, sizeof ubuf, ufmt, tp);
+ if (len == 0)
+ return 0;
+ cpy (len, ubuf);
+ }
+ break;
+#endif
+
case 'C': /* POSIX.2 extension. */
if (modifier == 'O')
goto bad_format;
-#if HAVE_STRUCT_ERA_ENTRY
if (modifier == 'E')
{
+#if HAVE_STRUCT_ERA_ENTRY
struct era_entry *era = _nl_get_era_entry (tp);
if (era)
{
cpy (len, era->name_fmt);
break;
}
- }
+#else
+# if HAVE_STRFTIME
+ goto underlying_strftime;
+# endif
#endif
+ }
+
{
int year = tp->tm_year + TM_YEAR_BASE;
DO_NUMBER (1, year / 100 - (year % 100 < 0));
&& *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
subfmt = _NL_CURRENT (LC_TIME, D_FMT);
goto subformat;
-#endif
+#else
+# if HAVE_STRFTIME
+ goto underlying_strftime;
+# else
/* Fall through. */
+# endif
+#endif
case 'D': /* POSIX.2 extension. */
if (modifier != 0)
goto bad_format;
do_number:
/* Format the number according to the MODIFIER flag. */
-#ifdef _NL_CURRENT
if (modifier == 'O' && 0 <= number_value)
{
+#ifdef _NL_CURRENT
/* Get the locale specific alternate representation of
the number NUMBER_VALUE. If none exist NULL is returned. */
const char *cp = _nl_get_alt_digit (number_value);
break;
}
}
- }
+#else
+# if HAVE_STRFTIME
+ goto underlying_strftime;
+# endif
#endif
+ }
{
unsigned int u = number_value;
cpy (buf + sizeof (buf) - bufp, bufp);
break;
+ case 'F':
+ if (modifier != 0)
+ goto bad_format;
+ subfmt = "%Y-%m-%d";
+ goto subformat;
case 'H':
if (modifier == 'E')
case 'P':
to_lowcase = 1;
+#if !defined _NL_CURRENT && HAVE_STRFTIME
+ format_char = 'p';
+#endif
/* FALLTHROUGH */
case 'p':
+ if (change_case)
+ {
+ to_uppcase = 0;
+ to_lowcase = 1;
+ }
+#if defined _NL_CURRENT || !HAVE_STRFTIME
cpy (ap_len, ampm);
break;
+#else
+ goto underlying_strftime;
+#endif
case 'R': /* GNU extension. */
subfmt = "%H:%M";
&& *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
subfmt = _NL_CURRENT (LC_TIME, T_FMT);
goto subformat;
-#endif
+#else
+# if HAVE_STRFTIME
+ goto underlying_strftime;
+# else
/* Fall through. */
+# endif
+#endif
case 'T': /* POSIX.2 extension. */
subfmt = "%H:%M:%S";
goto subformat;
add (1, *p = '\t');
break;
+ case 'f':
case 'u': /* POSIX.2 extension. */
DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
DO_NUMBER (1, tp->tm_wday);
case 'Y':
-#if HAVE_STRUCT_ERA_ENTRY
if (modifier == 'E')
{
+#if HAVE_STRUCT_ERA_ENTRY
struct era_entry *era = _nl_get_era_entry (tp);
if (era)
{
subfmt = strchr (era->name_fmt, '\0') + 1;
goto subformat;
}
- }
+#else
+# if HAVE_STRFTIME
+ goto underlying_strftime;
+# endif
#endif
+ }
if (modifier == 'O')
goto bad_format;
else
DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
case 'y':
-#if HAVE_STRUCT_ERA_ENTRY
if (modifier == 'E')
{
+#if HAVE_STRUCT_ERA_ENTRY
struct era_entry *era = _nl_get_era_entry (tp);
if (era)
{
DO_NUMBER (1, (era->offset
+ (era->direction == '-' ? -delta : delta)));
}
- }
+#else
+# if HAVE_STRFTIME
+ goto underlying_strftime;
+# endif
#endif
+ }
DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
case 'Z':
+ if (change_case)
+ {
+ to_uppcase = 0;
+ to_lowcase = 1;
+ }
cpy (zonelen, zone);
break;
valid time_t value. Check whether an error really
occurred. */
struct tm tm;
- localtime_r (<, &tm);
-
- if ((ltm.tm_sec ^ tm.tm_sec)
- | (ltm.tm_min ^ tm.tm_min)
- | (ltm.tm_hour ^ tm.tm_hour)
- | (ltm.tm_mday ^ tm.tm_mday)
- | (ltm.tm_mon ^ tm.tm_mon)
- | (ltm.tm_year ^ tm.tm_year))
+
+ if (! localtime_r (<, &tm)
+ || ((ltm.tm_sec ^ tm.tm_sec)
+ | (ltm.tm_min ^ tm.tm_min)
+ | (ltm.tm_hour ^ tm.tm_hour)
+ | (ltm.tm_mday ^ tm.tm_mday)
+ | (ltm.tm_mon ^ tm.tm_mon)
+ | (ltm.tm_year ^ tm.tm_year)))
break;
}