summaryrefslogtreecommitdiffstats
path: root/compat/strftime.c
diff options
context:
space:
mode:
authorcvs2fossil <cvs2fossil>2011-01-25 19:02:56 (GMT)
committercvs2fossil <cvs2fossil>2011-01-25 19:02:56 (GMT)
commit352fce86be9d102b2284de839b7f7ff94ed971f2 (patch)
treee454e0d4460f15029e4ed5ae3f3131a992445426 /compat/strftime.c
parent75f084f6970d2344bb5a82fdff6a73825bc6e64e (diff)
downloadtcl-dgp_refactor_merge.zip
tcl-dgp_refactor_merge.tar.gz
tcl-dgp_refactor_merge.tar.bz2
Created branch dgp-refactor-merge-syntheticdgp_refactor_mergedgp_refactor_merge_synthetic
Diffstat (limited to 'compat/strftime.c')
-rw-r--r--compat/strftime.c538
1 files changed, 538 insertions, 0 deletions
diff --git a/compat/strftime.c b/compat/strftime.c
new file mode 100644
index 0000000..91e7057
--- /dev/null
+++ b/compat/strftime.c
@@ -0,0 +1,538 @@
+/*
+ * strftime.c --
+ *
+ * This file contains a modified version of the BSD 4.4 strftime
+ * function.
+ *
+ * This file is a modified version of the strftime.c file from the BSD 4.4
+ * source. See the copyright notice below for details on redistribution
+ * restrictions. The "license.terms" file does not apply to this file.
+ *
+ * Changes 2002 Copyright (c) 2002 ActiveState Corporation.
+ *
+ * RCS: @(#) $Id: strftime.c,v 1.17 2004/09/08 18:46:18 kennykb Exp $
+ */
+
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS)
+static char *rcsid = "$Id: strftime.c,v 1.17 2004/09/08 18:46:18 kennykb Exp $";
+#endif /* LIBC_SCCS */
+
+#include <time.h>
+#include <string.h>
+#include <locale.h>
+#include "tclInt.h"
+
+#define TM_YEAR_BASE 1900
+#define IsLeapYear(x) ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0))
+
+/*
+ * Structure type holding the locale-dependent strings for formatting
+ * times. The strings are all expected to be encoded in UTF-8.
+ */
+
+typedef struct {
+ const char *abday[7]; /* Abbreviated weekday names */
+ const char *day[7]; /* Full weekday names */
+ const char *abmon[12]; /* Abbreviated month names */
+ const char *mon[12]; /* Full month name */
+ const char *am_pm[2]; /* The strings to use for meridian time
+ * (am, pm) */
+ const char *d_t_fmt; /* The locale-dependent date-time format */
+ const char *d_fmt; /* The locale-dependent date format */
+ const char *t_fmt; /* The locale-dependent 24hr time format */
+ const char *t_fmt_ampm; /* The locale-dependent 12hr time format */
+} _TimeLocale;
+
+/*
+ * This is the C locale default. On Windows, if we wanted to make this
+ * localized, we would use GetLocaleInfo to get the correct values.
+ * It may be acceptable to do localization of month/day names, as the
+ * numerical values would be considered the locale-independent versions.
+ */
+static const _TimeLocale _DefaultTimeLocale =
+{
+ {
+ "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
+ },
+ {
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday"
+ },
+ {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ },
+ {
+ "January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December"
+ },
+ {
+ "AM", "PM"
+ },
+ "%a %b %d %H:%M:%S %Y",
+ "%m/%d/%y",
+ "%H:%M:%S",
+ "%I:%M:%S %p"
+};
+
+static CONST _TimeLocale *_CurrentTimeLocale = &_DefaultTimeLocale;
+
+static int isGMT;
+static size_t gsize;
+static char *pt;
+static int _add _ANSI_ARGS_((CONST char* str));
+static int _conv _ANSI_ARGS_((int n, int digits, int pad));
+static int _secs _ANSI_ARGS_((CONST struct tm *t));
+static size_t _fmt _ANSI_ARGS_((CONST char *format,
+ const struct tm *t));
+static int ISO8601Week _ANSI_ARGS_((CONST struct tm* t, int *year ));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpStrftime --
+ *
+ * Formats a time (in 'struct tm' representation) for use by
+ * [clock format].
+ *
+ * Results:
+ * Returns the length of the formatted string.
+ *
+ * Side effects:
+ * Stores the formatted string in the 's' parameter. The
+ * formatted string is returned in UTF-8 encoding, *not* the
+ * system encoding.
+ *
+ *----------------------------------------------------------------------
+ */
+
+size_t
+TclpStrftime(s, maxsize, format, t, useGMT)
+ char *s; /* Buffer to hold the formatted string */
+ size_t maxsize; /* Size of the passed buffer */
+ CONST char *format; /* Format to use (see the user documentation
+ * for [clock] for details) */
+ CONST struct tm *t; /* Time to format */
+ int useGMT; /* Flag == 1 if time is to be returned
+ * in UTC */
+{
+ if (format[0] == '%' && format[1] == 'Q') {
+ /* Format as a stardate */
+ sprintf(s, "Stardate %2d%03d.%01d",
+ (((t->tm_year + TM_YEAR_BASE) + 377) - 2323),
+ (((t->tm_yday + 1) * 1000) /
+ (365 + IsLeapYear((t->tm_year + TM_YEAR_BASE)))),
+ (((t->tm_hour * 60) + t->tm_min)/144));
+ return(strlen(s));
+ }
+
+ isGMT = useGMT;
+ /*
+ * We may be able to skip this for useGMT, but it should be harmless.
+ * -- hobbs
+ */
+ tzset();
+
+ pt = s;
+ if ((gsize = maxsize) < 1)
+ return(0);
+ if (_fmt(format, t)) {
+ *pt = '\0';
+ return(maxsize - gsize);
+ }
+ return(0);
+}
+
+#define SUN_WEEK(t) (((t)->tm_yday + 7 - \
+ ((t)->tm_wday)) / 7)
+#define MON_WEEK(t) (((t)->tm_yday + 7 - \
+ ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
+
+static size_t
+_fmt(format, t)
+ CONST char *format;
+ CONST struct tm *t;
+{
+#ifdef __WIN32__
+#define BUF_SIZ 256
+ TCHAR buf[BUF_SIZ];
+ SYSTEMTIME syst;
+ syst.wYear = t->tm_year + 1900;
+ syst.wMonth = t->tm_mon + 1;
+ syst.wDayOfWeek = t->tm_wday;
+ syst.wDay = t->tm_mday;
+ syst.wHour = t->tm_hour;
+ syst.wMinute = t->tm_min;
+ syst.wSecond = t->tm_sec;
+ syst.wMilliseconds = 0;
+#endif
+ for (; *format; ++format) {
+ if (*format == '%') {
+ ++format;
+ if (*format == 'E') {
+ /* Alternate Era */
+ ++format;
+ } else if (*format == 'O') {
+ /* Alternate numeric symbols */
+ ++format;
+ }
+ switch(*format) {
+ case '\0':
+ --format;
+ break;
+ case 'A':
+ if (t->tm_wday < 0 || t->tm_wday > 6)
+ return(0);
+ if (!_add(_CurrentTimeLocale->day[t->tm_wday]))
+ return(0);
+ continue;
+ case 'a':
+ if (t->tm_wday < 0 || t->tm_wday > 6)
+ return(0);
+ if (!_add(_CurrentTimeLocale->abday[t->tm_wday]))
+ return(0);
+ continue;
+ case 'B':
+ if (t->tm_mon < 0 || t->tm_mon > 11)
+ return(0);
+ if (!_add(_CurrentTimeLocale->mon[t->tm_mon]))
+ return(0);
+ continue;
+ case 'b':
+ case 'h':
+ if (t->tm_mon < 0 || t->tm_mon > 11)
+ return(0);
+ if (!_add(_CurrentTimeLocale->abmon[t->tm_mon]))
+ return(0);
+ continue;
+ case 'C':
+ if (!_conv((t->tm_year + TM_YEAR_BASE) / 100,
+ 2, '0'))
+ return(0);
+ continue;
+ case 'D':
+ if (!_fmt("%m/%d/%y", t))
+ return(0);
+ continue;
+ case 'd':
+ if (!_conv(t->tm_mday, 2, '0'))
+ return(0);
+ continue;
+ case 'e':
+ if (!_conv(t->tm_mday, 2, ' '))
+ return(0);
+ continue;
+ case 'g':
+ {
+ int year;
+ ISO8601Week( t, &year );
+ if ( !_conv( year%100, 2, '0' ) ) {
+ return( 0 );
+ }
+ continue;
+ }
+ case 'G':
+ {
+ int year;
+ ISO8601Week( t, &year );
+ if ( !_conv( year, 4, '0' ) ) {
+ return( 0 );
+ }
+ continue;
+ }
+ case 'H':
+ if (!_conv(t->tm_hour, 2, '0'))
+ return(0);
+ continue;
+ case 'I':
+ if (!_conv(t->tm_hour % 12 ?
+ t->tm_hour % 12 : 12, 2, '0'))
+ return(0);
+ continue;
+ case 'j':
+ if (!_conv(t->tm_yday + 1, 3, '0'))
+ return(0);
+ continue;
+ case 'k':
+ if (!_conv(t->tm_hour, 2, ' '))
+ return(0);
+ continue;
+ case 'l':
+ if (!_conv(t->tm_hour % 12 ?
+ t->tm_hour % 12: 12, 2, ' '))
+ return(0);
+ continue;
+ case 'M':
+ if (!_conv(t->tm_min, 2, '0'))
+ return(0);
+ continue;
+ case 'm':
+ if (!_conv(t->tm_mon + 1, 2, '0'))
+ return(0);
+ continue;
+ case 'n':
+ if (!_add("\n"))
+ return(0);
+ continue;
+ case 'p':
+ if (!_add(_CurrentTimeLocale->am_pm[t->tm_hour >= 12]))
+ return(0);
+ continue;
+ case 'R':
+ if (!_fmt("%H:%M", t))
+ return(0);
+ continue;
+ case 'r':
+ if (!_fmt(_CurrentTimeLocale->t_fmt_ampm, t))
+ return(0);
+ continue;
+ case 'S':
+ if (!_conv(t->tm_sec, 2, '0'))
+ return(0);
+ continue;
+ case 's':
+ if (!_secs(t))
+ return(0);
+ continue;
+ case 'T':
+ if (!_fmt("%H:%M:%S", t))
+ return(0);
+ continue;
+ case 't':
+ if (!_add("\t"))
+ return(0);
+ continue;
+ case 'U':
+ if (!_conv(SUN_WEEK(t), 2, '0'))
+ return(0);
+ continue;
+ case 'u':
+ if (!_conv(t->tm_wday ? t->tm_wday : 7, 1, '0'))
+ return(0);
+ continue;
+ case 'V':
+ {
+ int week = ISO8601Week( t, NULL );
+ if (!_conv(week, 2, '0'))
+ return(0);
+ continue;
+ }
+ case 'W':
+ if (!_conv(MON_WEEK(t), 2, '0'))
+ return(0);
+ continue;
+ case 'w':
+ if (!_conv(t->tm_wday, 1, '0'))
+ return(0);
+ continue;
+#ifdef __WIN32__
+ /*
+ * To properly handle the localized time routines on Windows,
+ * we must make use of the special localized calls.
+ */
+ case 'c':
+ if (!GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE,
+ &syst, NULL, buf, BUF_SIZ) || !_add(buf)
+ || !_add(" ")) {
+ return(0);
+ }
+ /*
+ * %c is created with LONGDATE + " " + TIME on Windows,
+ * so continue to %X case here.
+ */
+ case 'X':
+ if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0,
+ &syst, NULL, buf, BUF_SIZ) || !_add(buf)) {
+ return(0);
+ }
+ continue;
+ case 'x':
+ if (!GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE,
+ &syst, NULL, buf, BUF_SIZ) || !_add(buf)) {
+ return(0);
+ }
+ continue;
+#else
+ case 'c':
+ if (!_fmt(_CurrentTimeLocale->d_t_fmt, t))
+ return(0);
+ continue;
+ case 'x':
+ if (!_fmt(_CurrentTimeLocale->d_fmt, t))
+ return(0);
+ continue;
+ case 'X':
+ if (!_fmt(_CurrentTimeLocale->t_fmt, t))
+ return(0);
+ continue;
+#endif
+ case 'y':
+ if (!_conv((t->tm_year + TM_YEAR_BASE) % 100,
+ 2, '0'))
+ return(0);
+ continue;
+ case 'Y':
+ if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0'))
+ return(0);
+ continue;
+ case 'Z': {
+ char *name = (isGMT ? "GMT" : TclpGetTZName(t->tm_isdst));
+ if (name && !_add(name)) {
+ return 0;
+ }
+ continue;
+ }
+ case '%':
+ /*
+ * X311J/88-090 (4.12.3.5): if conversion char is
+ * undefined, behavior is undefined. Print out the
+ * character itself as printf(3) does.
+ */
+ default:
+ break;
+ }
+ }
+ if (!gsize--)
+ return(0);
+ *pt++ = *format;
+ }
+ return(gsize);
+}
+
+static int
+_secs(t)
+ CONST struct tm *t;
+{
+ static char buf[15];
+ register time_t s;
+ register char *p;
+ struct tm tmp;
+
+ /* Make a copy, mktime(3) modifies the tm struct. */
+ tmp = *t;
+ s = mktime(&tmp);
+ for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10)
+ *p-- = (char)(s % 10 + '0');
+ return(_add(++p));
+}
+
+static int
+_conv(n, digits, pad)
+ int n, digits;
+ int pad;
+{
+ static char buf[10];
+ register char *p;
+
+ p = buf + sizeof( buf ) - 1;
+ *p-- = '\0';
+ if ( n == 0 ) {
+ *p-- = '0';
+ } else {
+ for (; n > 0 && p > buf; n /= 10, --digits)
+ *p-- = (char)(n % 10 + '0');
+ }
+ while (p > buf && digits-- > 0)
+ *p-- = (char) pad;
+ return(_add(++p));
+}
+
+static int
+_add(str)
+ const char *str;
+{
+ for (;; ++pt, --gsize) {
+ if (!gsize)
+ return(0);
+ if (!(*pt = *str++))
+ return(1);
+ }
+}
+
+static int
+ISO8601Week( t, year )
+ CONST struct tm* t;
+ int* year;
+{
+ /* Find the day-of-year of the Thursday in
+ * the week in question. */
+
+ int ydayThursday;
+ int week;
+ if ( t->tm_wday == 0 ) {
+ ydayThursday = t->tm_yday - 3;
+ } else {
+ ydayThursday = t->tm_yday - t->tm_wday + 4;
+ }
+
+ if ( ydayThursday < 0 ) {
+
+ /* This is the last week of the previous year. */
+ if ( IsLeapYear(( t->tm_year + TM_YEAR_BASE - 1 )) ) {
+ ydayThursday += 366;
+ } else {
+ ydayThursday += 365;
+ }
+ week = ydayThursday / 7 + 1;
+ if ( year != NULL ) {
+ *year = t->tm_year + 1899;
+ }
+
+ } else if ( ( IsLeapYear(( t -> tm_year + TM_YEAR_BASE ))
+ && ydayThursday >= 366 )
+ || ( !IsLeapYear(( t -> tm_year
+ + TM_YEAR_BASE ))
+ && ydayThursday >= 365 ) ) {
+
+ /* This is week 1 of the following year */
+
+ week = 1;
+ if ( year != NULL ) {
+ *year = t->tm_year + 1901;
+ }
+
+ } else {
+
+ week = ydayThursday / 7 + 1;
+ if ( year != NULL ) {
+ *year = t->tm_year + 1900;
+ }
+
+ }
+
+ return week;
+
+}