os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/compat/strftime.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * strftime.c --
     3  *
     4  *	This file contains a modified version of the BSD 4.4 strftime
     5  *	function.
     6  *
     7  * This file is a modified version of the strftime.c file from the BSD 4.4
     8  * source.  See the copyright notice below for details on redistribution
     9  * restrictions.  The "license.terms" file does not apply to this file.
    10  *
    11  * Changes 2002 Copyright (c) 2002 ActiveState Corporation.
    12  *
    13  * RCS: @(#) $Id: strftime.c,v 1.10.2.3 2005/11/04 18:18:04 kennykb Exp $
    14  */
    15 
    16 /*
    17  * Copyright (c) 1989 The Regents of the University of California.
    18  * All rights reserved.
    19  *
    20  * Redistribution and use in source and binary forms, with or without
    21  * modification, are permitted provided that the following conditions
    22  * are met:
    23  * 1. Redistributions of source code must retain the above copyright
    24  *    notice, this list of conditions and the following disclaimer.
    25  * 2. Redistributions in binary form must reproduce the above copyright
    26  *    notice, this list of conditions and the following disclaimer in the
    27  *    documentation and/or other materials provided with the distribution.
    28  * 3. All advertising materials mentioning features or use of this software
    29  *    must display the following acknowledgement:
    30  *	This product includes software developed by the University of
    31  *	California, Berkeley and its contributors.
    32  * 4. Neither the name of the University nor the names of its contributors
    33  *    may be used to endorse or promote products derived from this software
    34  *    without specific prior written permission.
    35  *
    36  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
    37  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    39  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
    40  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    41  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    42  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    44  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    45  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    46  * SUCH DAMAGE.
    47  */
    48 
    49 #if defined(LIBC_SCCS)
    50 static char *rcsid = "$Id: strftime.c,v 1.10.2.3 2005/11/04 18:18:04 kennykb Exp $";
    51 #endif /* LIBC_SCCS */
    52 
    53 #include <time.h>
    54 #include <string.h>
    55 #include <locale.h>
    56 #include "tclInt.h"
    57 #include "tclPort.h"
    58 
    59 #define TM_YEAR_BASE   1900
    60 #define IsLeapYear(x)   ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0))
    61 
    62 typedef struct {
    63     const char *abday[7];
    64     const char *day[7];
    65     const char *abmon[12];
    66     const char *mon[12];
    67     const char *am_pm[2];
    68     const char *d_t_fmt;
    69     const char *d_fmt;
    70     const char *t_fmt;
    71     const char *t_fmt_ampm;
    72 } _TimeLocale;
    73 
    74 /*
    75  * This is the C locale default.  On Windows, if we wanted to make this
    76  * localized, we would use GetLocaleInfo to get the correct values.
    77  * It may be acceptable to do localization of month/day names, as the
    78  * numerical values would be considered the locale-independent versions.
    79  */
    80 static const _TimeLocale _DefaultTimeLocale = 
    81 {
    82     {
    83 	"Sun","Mon","Tue","Wed","Thu","Fri","Sat",
    84     },
    85     {
    86 	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
    87 	"Friday", "Saturday"
    88     },
    89     {
    90 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
    91 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    92     },
    93     {
    94 	"January", "February", "March", "April", "May", "June", "July",
    95 	"August", "September", "October", "November", "December"
    96     },
    97     {
    98 	"AM", "PM"
    99     },
   100     "%a %b %d %H:%M:%S %Y",
   101     "%m/%d/%y",
   102     "%H:%M:%S",
   103     "%I:%M:%S %p"
   104 };
   105 
   106 static const _TimeLocale *_CurrentTimeLocale = &_DefaultTimeLocale;
   107 
   108 static int isGMT;
   109 static size_t gsize;
   110 static char *pt;
   111 static int		 _add _ANSI_ARGS_((const char* str));
   112 static int		_conv _ANSI_ARGS_((int n, int digits, int pad));
   113 static int		_secs _ANSI_ARGS_((const struct tm *t));
   114 static size_t		_fmt _ANSI_ARGS_((const char *format,
   115 			    const struct tm *t));
   116 static int ISO8601Week _ANSI_ARGS_((CONST struct tm* t, int *year ));
   117 
   118 size_t
   119 TclpStrftime(s, maxsize, format, t, useGMT)
   120     char *s;
   121     size_t maxsize;
   122     const char *format;
   123     const struct tm *t;
   124     int useGMT;
   125 {
   126     if (format[0] == '%' && format[1] == 'Q') {
   127 	/* Format as a stardate */
   128 	sprintf(s, "Stardate %2d%03d.%01d",
   129 		(((t->tm_year + TM_YEAR_BASE) + 377) - 2323),
   130 		(((t->tm_yday + 1) * 1000) /
   131 			(365 + IsLeapYear((t->tm_year + TM_YEAR_BASE)))),
   132 		(((t->tm_hour * 60) + t->tm_min)/144));
   133 	return(strlen(s));
   134     }
   135 
   136     isGMT = useGMT;
   137     /*
   138      * We may be able to skip this for useGMT, but it should be harmless.
   139      * -- hobbs
   140      */
   141     tzset();
   142 
   143     pt = s;
   144     if ((gsize = maxsize) < 1)
   145 	return(0);
   146     if (_fmt(format, t)) {
   147 	*pt = '\0';
   148 	return(maxsize - gsize);
   149     }
   150     return(0);
   151 }
   152 
   153 #define SUN_WEEK(t)	(((t)->tm_yday + 7 - \
   154 				((t)->tm_wday)) / 7)
   155 #define MON_WEEK(t)	(((t)->tm_yday + 7 - \
   156 				((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
   157 
   158 static size_t
   159 _fmt(format, t)
   160     const char *format;
   161     const struct tm *t;
   162 {
   163 #ifdef WIN32
   164 #define BUF_SIZ 256
   165     TCHAR buf[BUF_SIZ];
   166     SYSTEMTIME syst = {
   167 	t->tm_year + 1900,
   168 	t->tm_mon + 1,
   169 	t->tm_wday,
   170 	t->tm_mday,
   171 	t->tm_hour,
   172 	t->tm_min,
   173 	t->tm_sec,
   174 	0,
   175     };
   176 #endif
   177     for (; *format; ++format) {
   178 	if (*format == '%') {
   179 	    ++format;
   180 	    if (*format == 'E') {
   181 				/* Alternate Era */
   182 		++format;
   183 	    } else if (*format == 'O') {
   184 				/* Alternate numeric symbols */
   185 		++format;
   186 	    }
   187 	    switch(*format) {
   188 		case '\0':
   189 		    --format;
   190 		    break;
   191 		case 'A':
   192 		    if (t->tm_wday < 0 || t->tm_wday > 6)
   193 			return(0);
   194 		    if (!_add(_CurrentTimeLocale->day[t->tm_wday]))
   195 			return(0);
   196 		    continue;
   197 		case 'a':
   198 		    if (t->tm_wday < 0 || t->tm_wday > 6)
   199 			return(0);
   200 		    if (!_add(_CurrentTimeLocale->abday[t->tm_wday]))
   201 			return(0);
   202 		    continue;
   203 		case 'B':
   204 		    if (t->tm_mon < 0 || t->tm_mon > 11)
   205 			return(0);
   206 		    if (!_add(_CurrentTimeLocale->mon[t->tm_mon]))
   207 			return(0);
   208 		    continue;
   209 		case 'b':
   210 		case 'h':
   211 		    if (t->tm_mon < 0 || t->tm_mon > 11)
   212 			return(0);
   213 		    if (!_add(_CurrentTimeLocale->abmon[t->tm_mon]))
   214 			return(0);
   215 		    continue;
   216 		case 'C':
   217 		    if (!_conv((t->tm_year + TM_YEAR_BASE) / 100,
   218 			    2, '0'))
   219 			return(0);
   220 		    continue;
   221 		case 'D':
   222 		    if (!_fmt("%m/%d/%y", t))
   223 			return(0);
   224 		    continue;
   225 		case 'd':
   226 		    if (!_conv(t->tm_mday, 2, '0'))
   227 			return(0);
   228 		    continue;
   229 		case 'e':
   230 		    if (!_conv(t->tm_mday, 2, ' '))
   231 			return(0);
   232 		    continue;
   233 	        case 'g':
   234 		    {
   235 			int year;
   236 			ISO8601Week( t, &year );
   237 			if ( !_conv( year%100, 2, '0' ) ) {
   238 			    return( 0 );
   239 			}
   240 			continue;
   241 		    }
   242 	        case 'G':
   243 		    {
   244 			int year;
   245 			ISO8601Week( t, &year );
   246 			if ( !_conv( year, 4, '0' ) ) {
   247 			    return( 0 );
   248 			}
   249 			continue;
   250 		    }
   251 		case 'H':
   252 		    if (!_conv(t->tm_hour, 2, '0'))
   253 			return(0);
   254 		    continue;
   255 		case 'I':
   256 		    if (!_conv(t->tm_hour % 12 ?
   257 			    t->tm_hour % 12 : 12, 2, '0'))
   258 			return(0);
   259 		    continue;
   260 		case 'j':
   261 		    if (!_conv(t->tm_yday + 1, 3, '0'))
   262 			return(0);
   263 		    continue;
   264 		case 'k':
   265 		    if (!_conv(t->tm_hour, 2, ' '))
   266 			return(0);
   267 		    continue;
   268 		case 'l':
   269 		    if (!_conv(t->tm_hour % 12 ?
   270 			    t->tm_hour % 12: 12, 2, ' '))
   271 			return(0);
   272 		    continue;
   273 		case 'M':
   274 		    if (!_conv(t->tm_min, 2, '0'))
   275 			return(0);
   276 		    continue;
   277 		case 'm':
   278 		    if (!_conv(t->tm_mon + 1, 2, '0'))
   279 			return(0);
   280 		    continue;
   281 		case 'n':
   282 		    if (!_add("\n"))
   283 			return(0);
   284 		    continue;
   285 		case 'p':
   286 		    if (!_add(_CurrentTimeLocale->am_pm[t->tm_hour >= 12]))
   287 			return(0);
   288 		    continue;
   289 		case 'R':
   290 		    if (!_fmt("%H:%M", t))
   291 			return(0);
   292 		    continue;
   293 		case 'r':
   294 		    if (!_fmt(_CurrentTimeLocale->t_fmt_ampm, t))
   295 			return(0);
   296 		    continue;
   297 		case 'S':
   298 		    if (!_conv(t->tm_sec, 2, '0'))
   299 			return(0);
   300 		    continue;
   301 		case 's':
   302 		    if (!_secs(t))
   303 			return(0);
   304 		    continue;
   305 		case 'T':
   306 		    if (!_fmt("%H:%M:%S", t))
   307 			return(0);
   308 		    continue;
   309 		case 't':
   310 		    if (!_add("\t"))
   311 			return(0);
   312 		    continue;
   313 		case 'U':
   314 		    if (!_conv(SUN_WEEK(t), 2, '0'))
   315 			return(0);
   316 		    continue;
   317 		case 'u':
   318 		    if (!_conv(t->tm_wday ? t->tm_wday : 7, 1, '0'))
   319 			return(0);
   320 		    continue;
   321 		case 'V':
   322 		{
   323 		    int week = ISO8601Week( t, NULL );
   324 		    if (!_conv(week, 2, '0'))
   325 			return(0);
   326 		    continue;
   327 		}
   328 		case 'W':
   329 		    if (!_conv(MON_WEEK(t), 2, '0'))
   330 			return(0);
   331 		    continue;
   332 		case 'w':
   333 		    if (!_conv(t->tm_wday, 1, '0'))
   334 			return(0);
   335 		    continue;
   336 #ifdef WIN32
   337 		/*
   338 		 * To properly handle the localized time routines on Windows,
   339 		 * we must make use of the special localized calls.
   340 		 */
   341 		case 'c':
   342 		    if (!GetDateFormat(LOCALE_USER_DEFAULT,
   343 				       DATE_LONGDATE | LOCALE_USE_CP_ACP,
   344 				       &syst, NULL, buf, BUF_SIZ)
   345 			|| !_add(buf)
   346 			|| !_add(" ")) {
   347 			return(0);
   348 		    }
   349 		    /*
   350 		     * %c is created with LONGDATE + " " + TIME on Windows,
   351 		     * so continue to %X case here.
   352 		     */
   353 		case 'X':
   354 		    if (!GetTimeFormat(LOCALE_USER_DEFAULT,
   355 				       LOCALE_USE_CP_ACP,
   356 				       &syst, NULL, buf, BUF_SIZ)
   357 			|| !_add(buf)) {
   358 			return(0);
   359 		    }
   360 		    continue;
   361 		case 'x':
   362 		    if (!GetDateFormat(LOCALE_USER_DEFAULT,
   363 				       DATE_SHORTDATE | LOCALE_USE_CP_ACP,
   364 				       &syst, NULL, buf, BUF_SIZ)
   365 			|| !_add(buf)) {
   366 			return(0);
   367 		    }
   368 		    continue;
   369 #else
   370 		case 'c':
   371 		    if (!_fmt(_CurrentTimeLocale->d_t_fmt, t))
   372 			return(0);
   373 		    continue;
   374 		case 'x':
   375 		    if (!_fmt(_CurrentTimeLocale->d_fmt, t))
   376 			return(0);
   377 		    continue;
   378 		case 'X':
   379 		    if (!_fmt(_CurrentTimeLocale->t_fmt, t))
   380 			return(0);
   381 		    continue;
   382 #endif
   383 		case 'y':
   384 		    if (!_conv((t->tm_year + TM_YEAR_BASE) % 100,
   385 			    2, '0'))
   386 			return(0);
   387 		    continue;
   388 		case 'Y':
   389 		    if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0'))
   390 			return(0);
   391 		    continue;
   392 		case 'Z': {
   393 		    char *name = (isGMT ? "GMT" : TclpGetTZName(t->tm_isdst));
   394 		    int wrote;
   395 		    Tcl_UtfToExternal(NULL, NULL, name, -1, 0, NULL,
   396 				      pt, gsize, NULL, &wrote, NULL);
   397 		    pt += wrote;
   398 		    gsize -= wrote;
   399 		    continue;
   400 		}
   401 		case '%':
   402 		    /*
   403 		     * X311J/88-090 (4.12.3.5): if conversion char is
   404 		     * undefined, behavior is undefined.  Print out the
   405 		     * character itself as printf(3) does.
   406 		     */
   407 		default:
   408 		    break;
   409 	    }
   410 	}
   411 	if (!gsize--)
   412 	    return(0);
   413 	*pt++ = *format;
   414     }
   415     return(gsize);
   416 }
   417 
   418 static int
   419 _secs(t)
   420     const struct tm *t;
   421 {
   422     static char buf[15];
   423     register time_t s;
   424     register char *p;
   425     struct tm tmp;
   426 
   427     /* Make a copy, mktime(3) modifies the tm struct. */
   428     tmp = *t;
   429     s = mktime(&tmp);
   430     for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10)
   431 	*p-- = (char)(s % 10 + '0');
   432     return(_add(++p));
   433 }
   434 
   435 static int
   436 _conv(n, digits, pad)
   437     int n, digits;
   438     int pad;
   439 {
   440     static char buf[10];
   441     register char *p;
   442 
   443     p = buf + sizeof( buf ) - 1;
   444     *p-- = '\0';
   445     if ( n == 0 ) {
   446 	*p-- = '0'; --digits;
   447     } else {
   448 	for (; n > 0 && p > buf; n /= 10, --digits)
   449 	    *p-- = (char)(n % 10 + '0');
   450     }
   451     while (p > buf && digits-- > 0)
   452 	*p-- = (char) pad;
   453     return(_add(++p));
   454 }
   455 
   456 static int
   457 _add(str)
   458     const char *str;
   459 {
   460     for (;; ++pt, --gsize) {
   461 	if (!gsize)
   462 	    return(0);
   463 	if (!(*pt = *str++))
   464 	    return(1);
   465     }
   466 }
   467 
   468 static int
   469 ISO8601Week( t, year )
   470     CONST struct tm* t;
   471     int* year;
   472 {
   473     /* Find the day-of-year of the Thursday in
   474      * the week in question. */
   475     
   476     int ydayThursday;
   477     int week;
   478     if ( t->tm_wday == 0 ) {
   479 	ydayThursday = t->tm_yday - 3;
   480     } else {
   481 	ydayThursday = t->tm_yday - t->tm_wday + 4;
   482     }
   483     
   484     if ( ydayThursday < 0 ) {
   485 	
   486 	/* This is the last week of the previous year. */
   487 	if ( IsLeapYear(( t->tm_year + TM_YEAR_BASE - 1 )) ) {
   488 	    ydayThursday += 366;
   489 	} else {
   490 	    ydayThursday += 365;
   491 	}
   492 	week = ydayThursday / 7 + 1;
   493 	if ( year != NULL ) {
   494 	    *year = t->tm_year + 1899;
   495 	}
   496 	
   497     } else if ( ( IsLeapYear(( t -> tm_year + TM_YEAR_BASE ))
   498 		  && ydayThursday >= 366 )
   499 		|| ( !IsLeapYear(( t -> tm_year
   500 				   + TM_YEAR_BASE ))
   501 		     && ydayThursday >= 365 ) ) {
   502 	
   503 	/* This is week 1 of the following year */
   504 	
   505 	week = 1;
   506 	if ( year != NULL ) {
   507 	    *year = t->tm_year + 1901;
   508 	}
   509 	
   510     } else {
   511 	
   512 	week = ydayThursday / 7 + 1;
   513 	if ( year != NULL ) {
   514 	    *year = t->tm_year + 1900;
   515 	}
   516 	
   517     }
   518 
   519     return week;
   520     
   521 }