-/* 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.
+ 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
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. */
+ 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>
# 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. */
# 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
-# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+# if defined (PROTOTYPES)
# define __P(args) args
# else
# define __P(args) ()
add one for integer division truncation;
add one more for a minus sign if t is signed. */
#define INT_STRLEN_BOUND(t) \
- ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
+ ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
#define TM_YEAR_BASE 1900
#ifdef _LIBC
-# define gmtime_r __gmtime_r
-# define localtime_r __localtime_r
+# define my_strftime_gmtime_r __gmtime_r
+# define my_strftime_localtime_r __localtime_r
# 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
-static struct tm *gmtime_r __P ((const time_t *, struct tm *));
+
+/* If we're a strftime substitute in a GNU program, then prefer gmtime
+ to gmtime_r, since many gmtime_r implementations are buggy.
+ Similarly for localtime_r. */
+
+# if ! HAVE_TM_GMTOFF
+static struct tm *my_strftime_gmtime_r __P ((const time_t *, struct tm *));
static struct tm *
-gmtime_r (t, tp)
+my_strftime_gmtime_r (t, tp)
const time_t *t;
struct tm *tp;
{
*tp = *l;
return tp;
}
-# endif /* ! HAVE_TM_GMTOFF */
+# endif /* ! HAVE_TM_GMTOFF */
-/* Approximate localtime_r as best we can in its absence. */
-# define localtime_r my_ftime_localtime_r
-static struct tm *localtime_r __P ((const time_t *, struct tm *));
+static struct tm *my_strftime_localtime_r __P ((const time_t *, struct tm *));
static struct tm *
-localtime_r (t, tp)
+my_strftime_localtime_r (t, tp)
const time_t *t;
struct tm *tp;
{
*tp = *l;
return tp;
}
-# endif /* ! HAVE_LOCALTIME_R */
-#endif /* ! defined (_LIBC) */
+#endif /* ! defined _LIBC */
#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
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); \
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;
}
#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_strftimeu
+# define ut_argument , ut
+# define ut_argument_spec int ut;
+# define ut_argument_spec_iso , int ut
+#else
+# define my_strftime strftime
+# define ut_argument
+# define ut_argument_spec
+# define ut_argument_spec_iso
+/* We don't have this information in general. */
+# define ut 0
+#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 *));
+ const struct tm * ut_argument_spec_iso));
size_t
- strftime (s, maxsize, format, tp)
+ my_strftime (s, maxsize, format, tp ut_argument)
char *s;
size_t maxsize;
const char *format;
const struct tm *tp;
+ ut_argument_spec
{
struct tm tmcopy;
tmcopy = *tp;
- return _strftime_copytm (s, maxsize, format, &tmcopy);
+ return _strftime_copytm (s, maxsize, format, &tmcopy ut_argument);
}
-# ifdef strftime
-# undef strftime
-# endif
-# define strftime(S, Maxsize, Format, Tp) \
- _strftime_copytm (S, Maxsize, Format, Tp)
+# undef my_strftime
+# define my_strftime _strftime_copytm
#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 ut_argument)
char *s;
size_t maxsize;
const char *format;
const struct tm *tp;
+ ut_argument_spec
{
int hour12 = tp->tm_hour;
#ifdef _NL_CURRENT
- const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
- const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
- const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
- const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
- const char *const ampm = _NL_CURRENT (LC_TIME,
- hour12 > 11 ? PM_STR : AM_STR);
- size_t aw_len = strlen (a_wkday);
- size_t am_len = strlen (a_month);
- size_t ap_len = strlen (ampm);
+ /* We cannot make the following values variables since we must delay
+ the evaluation of these values until really needed since some
+ expressions might not be valid in every situation. The `struct tm'
+ might be generated by a strptime() call that initialized
+ only a few elements. Dereference the pointers only if the format
+ requires this. Then it is ok to fail if the pointers are invalid. */
+# define a_wkday _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday)
+# define f_wkday _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday)
+# define a_month _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon)
+# define f_month _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon)
+# define ampm _NL_CURRENT (LC_TIME, tp->tm_hour > 11 ? PM_STR : AM_STR)
+
+# define aw_len strlen (a_wkday)
+# define am_len strlen (a_month)
+# define ap_len strlen (ampm)
#else
- 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;
- const char *const a_month = f_month;
- const char *const ampm = "AMPM" + 2 * (hour12 > 11);
+# if !HAVE_STRFTIME
+# define f_wkday (weekday_name[tp->tm_wday])
+# define f_month (month_name[tp->tm_mon])
+# define a_wkday f_wkday
+# define a_month f_month
+# define ampm ("AMPM" + 2 * (tp->tm_hour > 11))
+
size_t aw_len = 3;
size_t am_len = 3;
size_t ap_len = 2;
+# endif
#endif
- size_t wkday_len = strlen (f_wkday);
- size_t month_len = strlen (f_month);
const char *zone;
- size_t zonelen;
size_t i = 0;
char *p = s;
const char *f;
zone = (const char *) tp->tm_zone;
#endif
#if HAVE_TZNAME
- /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
- time zone names contained in the external variable `tzname' shall
- be set as if the tzset() function had been called. */
+ if (ut)
+ {
+ if (! (zone && *zone))
+ zone = "GMT";
+ }
+ else
+ {
+ /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
+ time zone names contained in the external variable `tzname' shall
+ be set as if the tzset() function had been called. */
# if HAVE_TZSET
- tzset ();
+ tzset ();
# endif
-
- if (!(zone && *zone) && tp->tm_isdst >= 0)
- zone = tzname[tp->tm_isdst];
+ }
#endif
- if (! zone)
- zone = ""; /* POSIX.2 requires the empty string here. */
-
- zonelen = strlen (zone);
if (hour12 > 12)
hour12 -= 12;
else
- if (hour12 == 0) hour12 = 12;
+ if (hour12 == 0)
+ hour12 = 12;
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 to_lowcase = 0;
int to_uppcase = 0;
int change_case = 0;
+ int format_char;
#if DO_MULTIBYTE
case '%':
break;
- case '\a': case '\b': case '\t': case '\n':
+ case '\b': case '\t': case '\n':
case '\v': case '\f': case '\r':
case ' ': case '!': case '"': case '#': case '&': case'\'':
case '(': case ')': case '*': case '+': case ',': case '-':
case 'r': case 's': case 't': case 'u': case 'v': case 'w':
case 'x': case 'y': case 'z': case '{': case '|': case '}':
case '~':
- /* The C Standard requires these 98 characters (plus '%') to
+ /* The C Standard requires these 97 characters (plus '%', '\a') to
be in the basic execution character set. None of these
characters can start a multibyte sequence, so they need
- not be analyzed further. */
+ not be analyzed further. Some old compilers object to
+ '\a', so don't bother optimizing for it. */
add (1, *p = *f);
continue;
if (bytes == 0)
break;
- if (bytes == (size_t) -2 || bytes == (size_t) -1)
+ if (bytes == (size_t) -2)
+ {
+ len += strlen (f + len);
+ break;
+ }
+
+ if (bytes == (size_t) -1)
{
len++;
break;
while (! mbsinit (&mbstate));
cpy (len, f);
+ f += len - 1;
continue;
}
}
#endif /* ! DO_MULTIBYTE */
/* Check for flags that can modify a format. */
- pad = 0;
while (1)
{
switch (*++f)
}
/* Now do the specified format. */
- switch (*f)
+ format_char = *f;
+ switch (format_char)
{
#define DO_NUMBER(d, v) \
digits = width == -1 ? d : width; \
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)
to_uppcase = 1;
to_lowcase = 0;
}
- cpy (wkday_len, f_wkday);
+#if defined _NL_CURRENT || !HAVE_STRFTIME
+ cpy (strlen (f_wkday), 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)
to_uppcase = 1;
to_lowcase = 0;
}
- cpy (month_len, f_month);
+#if defined _NL_CURRENT || !HAVE_STRFTIME
+ cpy (strlen (f_month), 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);
- if (len == 0 && *subfmt)
- return 0;
- add (len, strftime (p, maxsize - i, subfmt, tp));
+ size_t len = my_strftime (NULL, (size_t) -1, subfmt,
+ tp ut_argument);
+ add (len, my_strftime (p, maxsize - i, subfmt, tp ut_argument));
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 && ubuf[0] != '\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':
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;
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':
to_uppcase = 0;
to_lowcase = 1;
}
- cpy (zonelen, zone);
+
+#if HAVE_TZNAME
+ /* The tzset() call might have changed the value. */
+ if (!(zone && *zone) && tp->tm_isdst >= 0)
+ zone = tzname[tp->tm_isdst];
+#endif
+ if (! zone)
+ zone = ""; /* POSIX.2 requires the empty string here. */
+
+ cpy (strlen (zone), zone);
break;
case 'z': /* GNU extension. */
#if HAVE_TM_GMTOFF
diff = tp->tm_gmtoff;
#else
- struct tm gtm;
- struct tm ltm;
- time_t lt;
+ if (ut)
+ diff = 0;
+ else
+ {
+ struct tm gtm;
+ struct tm ltm;
+ time_t lt;
- ltm = *tp;
- lt = mktime (<m);
+ ltm = *tp;
+ lt = mktime (<m);
- if (lt == (time_t) -1)
- {
- /* mktime returns -1 for errors, but -1 is also a
- 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))
- break;
- }
+ if (lt == (time_t) -1)
+ {
+ /* mktime returns -1 for errors, but -1 is also a
+ valid time_t value. Check whether an error really
+ occurred. */
+ struct tm tm;
+
+ if (! my_strftime_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;
+ }
- if (! gmtime_r (<, >m))
- break;
+ if (! my_strftime_gmtime_r (<, >m))
+ break;
- diff = tm_diff (<m, >m);
+ diff = tm_diff (<m, >m);
+ }
#endif
if (diff < 0)
}
}
- if (p)
+ if (p && maxsize != 0)
*p = '\0';
return i;
}