/*** *strftime.c - String Format Time * * Copyright (c) Microsoft Corporation. All rights reserved. * *Purpose: * *Revision History: * 03-09-89 JCR Initial version. * 03-15-89 JCR Changed day/month strings from all caps to leading cap * 06-20-89 JCR Removed _LOAD_DGROUP code * 03-20-90 GJF Replaced _LOAD_DS with _CALLTYPE1, added #include * , removed #include and * removed some leftover 16-bit support. Also, fixed * the copyright. * 03-23-90 GJF Made static functions _CALLTYPE4. * 07-23-90 SBM Compiles cleanly with -W3 (removed unreferenced * variable) * 08-13-90 SBM Compiles cleanly with -W3 with new build of compiler * 10-04-90 GJF New-style function declarators. * 01-22-91 GJF ANSI naming. * 08-15-91 MRM Calls tzset() to set timezone info in case of %z. * 08-16-91 MRM Put appropriate header file for tzset(). * 10-10-91 ETC Locale support under _INTL switch. * 12-18-91 ETC Use localized time strings structure. * 02-10-93 CFW Ported to Cuda tree, change _CALLTYPE4 to _CRTAPI3. * 02-16-93 CFW Massive changes: bug fixes & enhancements. * 03-08-93 CFW Changed _expand to _expandtime. * 03-09-93 CFW Handle string literals inside format strings. * 03-09-93 CFW Alternate form cleanup. * 03-17-93 CFW Change *count > 0, to *count != 0, *count is unsigned. * 03-22-93 CFW Change "C" locale time format specifier to 24-hour. * 03-30-93 GJF Call _tzset instead of __tzset (which no longer * exists). * 04-06-93 SKS Replace _CRTAPI* with __cdecl * 04-14-93 CFW Disable _alternate_form for 'X' specifier, fix count bug. * 04-28-93 CFW Fix bug in '%c' handling. * 07-15-93 GJF Call __tzset() in place of _tzset(). * 09-15-93 CFW Use ANSI conformant "__" names. * 04-11-94 GJF Made definitions of __lc_time_c, _alternate_form and * _no_lead_zeros conditional on ndef DLL_FOR_WIN32S. * 09-06-94 CFW Remove _INTL switch. * 02-13-95 GJF Appended Mac version of source file (somewhat cleaned * up), with appropriate #ifdef-s. * 09-26-95 GJF New locking macro, and scheme, for functions which * reference the locale. * 02-22-96 JWM Merge in PlumHall mods. * 06-17-96 SKS Enable new Plum-Hall code for _MAC as well as _WIN32 * 07-10-97 GJF Made __lc_time_c selectany. Also, removed unnecessary * init to 0 for globals, cleaned up the formatting a bit, * added a few __cdecls and detailed old (and no longer * used as of 6/17/96 change) Mac version. * 08-21-97 GJF Added support for AM/PM type suffix to time string. * 09-10-98 GJF Added support for per-thread locale info. * 03-04-99 GJF Added refcount field to __lc_time_c. * 05-17-99 PML Remove all Macintosh support. * 08-30-99 PML Don't overflow buffer on leadbyte in _store_winword. * 03-17-00 PML Corrected _Gettnames to also copy ww_timefmt (VS7#9374) * 09-07-00 PML Remove dependency on libcp.lib/xlocinfo.h (vs7#159463) * 03-25-01 PML Use GetDateFormat/GetTimeFormat in _store_winword for * calendar types other than the basic type 1, localized * Gregorian (vs7#196892) Also fix formatting for leading * zero suppression in fields, which was busted for %c, * %x, %X. * 11-12-01 GB Added support for new locale implementation. * 12-11-01 BWT Replace _getptd with _getptd_noexit - we can return 0/ENOMEM * here instead of exiting. * 02-25-02 BWT Early exit expandtime if NULL is passed in as the timeptr * 05-07-02 GB Added fix for am:pm in spanish locale * 05-08-02 MSL Fixed MBCS leadbyte followed by \0 issues * Added error-return path from internal functions * Fixed one case of multi-threadunsafe isleadbyte * VS7 340068 * 03-06-03 SSM Assertions and validations * 03-23-03 SSM VSWhidbey 173159. Remove dead code. * 09-22-04 AGH Assert and set error code when dest buffer is too small * 09-25-04 JL Replace usage of _alloca() with _alloca_s() / _freea_s() * 10-22-04 AGH In _Strftime_l, fix validations * 02-16-05 AC Just return ERANGE if there is not enough space in strftime * VSW#454336 * 04-04-05 JL Replace _alloca_s and _freea_s with _malloca and _freea * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32_WCE #undef __LC_TIME_CURR #define __LC_TIME_CURR(ptloci) __lc_time_curr extern threadlocinfo __initiallocinfo; #define _isleadbyte_l(c, plocinfo) (__initiallocinfo.pctype[(c)] & _LEADBYTE) #endif /* _WIN32_WCE */ /* Prototypes for local routines */ static BOOL __cdecl _expandtime( #ifndef _WIN32_WCE _locale_t plocinfo, #endif /* _WIN32_WCE */ char specifier, const struct tm *tmptr, char **out, size_t *count, struct __lc_time_data *lc_time, unsigned alternate_form); static void __cdecl _store_str (char *in, char **out, size_t *count); static void __cdecl _store_num (int num, int digits, char **out, size_t *count, unsigned no_lead_zeros); static void __cdecl _store_number (int num, char **out, size_t *count); static BOOL __cdecl _store_winword ( #ifndef _WIN32_WCE _locale_t plocinfo, #endif /* _WIN32_WCE */ int field_code, const struct tm *tmptr, char **out, size_t *count, struct __lc_time_data *lc_time); #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ size_t __cdecl _Strftime ( char *string, size_t maxsize, const char *format, const struct tm *timeptr, void *lc_time_arg ); #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ size_t __cdecl _Strftime_l ( char *string, size_t maxsize, const char *format, const struct tm *timeptr, void *lc_time_arg #ifndef _WIN32_WCE ,_locale_t plocinfo #endif /* _WIN32_WCE */ ); /* LC_TIME data for local "C" */ #ifndef _WIN32_WCE extern "C" { #endif /* _WIN32_WCE */ __declspec(selectany) struct __lc_time_data __lc_time_c = { {"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"}, { "MM/dd/yy" }, { "dddd, MMMM dd, yyyy" }, { "HH:mm:ss" }, 0x0409, 1, #ifdef _MT 0 #endif }; /* Pointer to the current LC_TIME data structure. */ struct __lc_time_data *__lc_time_curr = &__lc_time_c; #ifndef _WIN32_WCE } #endif /* _WIN32_WCE */ /* Codes for __lc_time_data ww_* fields for _store_winword */ #define WW_SDATEFMT 0 #define WW_LDATEFMT 1 #define WW_TIMEFMT 2 #define TIME_SEP ':' /* get a copy of the current day names */ #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ char * __cdecl _Getdays_l ( #ifndef _WIN32_WCE _locale_t plocinfo #endif /* _WIN32_WCE */ ) { const struct __lc_time_data *pt; size_t n, len = 0; char *p; #ifndef _WIN32_WCE _LocaleUpdate _loc_update(plocinfo); #endif /* _WIN32_WCE */ pt = __LC_TIME_CURR(_loc_update.GetLocaleT()->locinfo); for (n = 0; n < 7; ++n) len += strlen(pt->wday_abbr[n]) + strlen(pt->wday[n]) + 2; p = (char *)_malloc_crt(len + 1); if (p != 0) { char *s = p; for (n = 0; n < 7; ++n) { *s++ = TIME_SEP; _ERRCHECK(strcpy_s(s, (len + 1) - (s - p), pt->wday_abbr[n])); s += strlen(s); *s++ = TIME_SEP; _ERRCHECK(strcpy_s(s, (len + 1) - (s - p), pt->wday[n])); s += strlen(s); } *s++ = '\0'; } return (p); } #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ char * __cdecl _Getdays ( void ) { return _Getdays_l(NULL); } /* get a copy of the current month names */ #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ char * __cdecl _Getmonths_l ( #ifndef _WIN32_WCE _locale_t plocinfo #endif /* _WIN32_WCE */ ) { const struct __lc_time_data *pt; size_t n, len = 0; char *p; #ifndef _WIN32_WCE _LocaleUpdate _loc_update(plocinfo); #endif /* _WIN32_WCE */ pt = __LC_TIME_CURR(_loc_update.GetLocaleT()->locinfo); for (n = 0; n < 12; ++n) len += strlen(pt->month_abbr[n]) + strlen(pt->month[n]) + 2; p = (char *)_malloc_crt(len + 1); if (p != 0) { char *s = p; for (n = 0; n < 12; ++n) { *s++ = TIME_SEP; _ERRCHECK(strcpy_s(s, (len + 1) - (s - p), pt->month_abbr[n])); s += strlen(s); *s++ = TIME_SEP; _ERRCHECK(strcpy_s(s, (len + 1) - (s - p), pt->month[n])); s += strlen(s); } *s++ = '\0'; } return (p); } #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ char * __cdecl _Getmonths ( void ) { return _Getmonths_l(NULL); } /* get a copy of the current time locale information */ #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ void * __cdecl _Gettnames_l ( #ifndef _WIN32_WCE _locale_t plocinfo #endif /* _WIN32_WCE */ ) { const struct __lc_time_data *pt; size_t n, len = 0; char *p; #ifndef _WIN32_WCE _LocaleUpdate _loc_update(plocinfo); #endif /* _WIN32_WCE */ pt = __LC_TIME_CURR(_loc_update.GetLocaleT()->locinfo); for (n = 0; n < 7; ++n) len += strlen(pt->wday_abbr[n]) + strlen(pt->wday[n]) + 2; for (n = 0; n < 12; ++n) len += strlen(pt->month_abbr[n]) + strlen(pt->month[n]) + 2; len += strlen(pt->ampm[0]) + strlen(pt->ampm[1]) + 2; len += strlen(pt->ww_sdatefmt) + 1; len += strlen(pt->ww_ldatefmt) + 1; len += strlen(pt->ww_timefmt) + 1; len += sizeof (*pt); p = (char *)_malloc_crt(len); if (p != 0) { struct __lc_time_data *pn = (struct __lc_time_data *)p; char *s = (char *)p + sizeof (*pt); memcpy(p, pt, sizeof (*pt)); for (n = 0; n < 7; ++n) { pn->wday_abbr[n] = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->wday_abbr[n])); s += strlen(s) + 1; pn->wday[n] = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->wday[n])); s += strlen(s) + 1; } for (n = 0; n < 12; ++n) { pn->month_abbr[n] = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->month_abbr[n])); s += strlen(s) + 1; pn->month[n] = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->month[n])); s += strlen(s) + 1; } pn->ampm[0] = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->ampm[0])); s += strlen(s) + 1; pn->ampm[1] = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->ampm[1])); s += strlen(s) + 1; pn->ww_sdatefmt = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->ww_sdatefmt)); s += strlen(s) + 1; pn->ww_ldatefmt = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->ww_ldatefmt)); s += strlen(s) + 1; pn->ww_timefmt = s; _ERRCHECK(strcpy_s(s, len - (s - p), pt->ww_timefmt)); } return (p); } #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ void * __cdecl _Gettnames ( void ) { return _Gettnames_l(NULL); } /*** *size_t strftime(string, maxsize, format, timeptr) - Format a time string * *Purpose: * Place characters into the user's output buffer expanding time * format directives as described in the user's control string. * Use the supplied 'tm' structure for time data when expanding * the format directives. * [ANSI] * *Entry: * char *string = pointer to output string * size_t maxsize = max length of string * const char *format = format control string * const struct tm *timeptr = pointer to tb data structure * *Exit: * !0 = If the total number of resulting characters including the * terminating null is not more than 'maxsize', then return the * number of chars placed in the 'string' array (not including the * null terminator). * * 0 = Otherwise, return 0 and the contents of the string are * indeterminate. * *Exceptions: * *******************************************************************************/ #ifndef _WIN32_WCE extern "C" size_t __cdecl _strftime_l ( char *string, size_t maxsize, const char *format, const struct tm *timeptr, _locale_t plocinfo ) { return (_Strftime_l(string, maxsize, format, timeptr, 0, plocinfo)); } extern "C" size_t __cdecl strftime ( char *string, size_t maxsize, const char *format, const struct tm *timeptr ) { return (_Strftime_l(string, maxsize, format, timeptr, 0, NULL)); } #endif /* _WIN32_WCE */ /*** *size_t _Strftime(string, maxsize, format, * timeptr, lc_time) - Format a time string for a given locale * *Purpose: * Place characters into the user's output buffer expanding time * format directives as described in the user's control string. * Use the supplied 'tm' structure for time data when expanding * the format directives. use the locale information at lc_time. * [ANSI] * *Entry: * char *string = pointer to output string * size_t maxsize = max length of string * const char *format = format control string * const struct tm *timeptr = pointer to tb data structure * struct __lc_time_data *lc_time = pointer to locale-specific info * (passed as void * to avoid type mismatch with C++) * *Exit: * !0 = If the total number of resulting characters including the * terminating null is not more than 'maxsize', then return the * number of chars placed in the 'string' array (not including the * null terminator). * * 0 = Otherwise, return 0 and the contents of the string are * indeterminate. * *Exceptions: * *******************************************************************************/ #ifndef _WIN32_WCE extern "C" size_t __cdecl _Strftime ( char *string, size_t maxsize, const char *format, const struct tm *timeptr, void *lc_time_arg ) { return _Strftime_l(string, maxsize, format, timeptr, lc_time_arg, NULL); } #endif /* _WIN32_WCE */ #ifndef _WIN32_WCE extern "C" #endif /* _WIN32_WCE */ size_t __cdecl _Strftime_l ( char *string, size_t maxsize, const char *format, const struct tm *timeptr, void *lc_time_arg #ifndef _WIN32_WCE ,_locale_t plocinfo #endif /* _WIN32_WCE */ ) { unsigned alternate_form; struct __lc_time_data *lc_time; BOOL failed=FALSE; /* true if a failure was reported to us */ size_t left; /* space left in output string */ char* strstart = string; #ifndef _WIN32_WCE _LocaleUpdate _loc_update(plocinfo); #endif /* _WIN32_WCE */ _VALIDATE_RETURN( ( string != NULL ), EINVAL, 0) _VALIDATE_RETURN( ( maxsize != 0 ), EINVAL, 0) *string = '\0'; _VALIDATE_RETURN( ( format != NULL ), EINVAL, 0) // Validated below // _VALIDATE_RETURN( ( timeptr != NULL ), EINVAL, 0) #ifndef _WIN32_WCE lc_time = lc_time_arg == 0 ? _loc_update.GetLocaleT()->locinfo->lc_time_curr : (struct __lc_time_data *)lc_time_arg; #else /* _WIN32_WCE */ lc_time = lc_time_arg == 0 ? __lc_time_curr : (struct __lc_time_data *)lc_time_arg; #endif /* _WIN32_WCE */ /* Copy maxsize into temp. */ left = maxsize; /* Copy the input string to the output string expanding the format designations appropriately. Stop copying when one of the following is true: (1) we hit a null char in the input stream, or (2) there's no room left in the output stream. */ while (left > 0) { switch(*format) { case('\0'): /* end of format input string */ goto done; case('%'): /* Format directive. Take appropriate action based on format control character. */ if (!timeptr) { _ASSERTE(timeptr!=0); #ifndef _WIN32_WCE errno = EINVAL; #endif /* _WIN32_WCE */ return 0; } format++; /* skip over % char */ /* process flags */ alternate_form = 0; if (*format == '#') { alternate_form = 1; format++; } #ifndef _WIN32_WCE if(!_expandtime (_loc_update.GetLocaleT(), *format, timeptr, &string, &left,lc_time, alternate_form)) #else /* _WIN32_WCE */ if(!_expandtime (*format, timeptr, &string, &left,lc_time, alternate_form)) #endif /* _WIN32_WCE */ { /* if we don't have any space left, do not set the failure flag: * we will simply return ERANGE and do not call _invalid_parameter_handler * (see below) */ if (left > 0) { failed=TRUE; } goto done; } format++; /* skip format char */ break; default: /* store character, bump pointers, dec the char count */ if( _isleadbyte_l((int)(*format), _loc_update.GetLocaleT()) && left > 1) { /* catch \0 directly following leadbyte - invalid MBCS sequence, and do not copy to output */ if(format[1]=='\0') { _ASSERTE(("Invalid MBCS character sequence passed to strftime",0)); failed=TRUE; goto done; } else { *string++ = *format++; left--; } } *string++ = *format++; left--; break; } } /* All done. See if we terminated because we hit a null char or because we ran out of space */ done: if (!failed && left > 0) { /* Store a terminating null char and return the number of chars we stored in the output string. */ *string = '\0'; return(maxsize-left); } else { /* error - return an empty string */ *(strstart)='\0'; /* now return our error/insufficient buffer indication */ if ( !failed && left <= 0 ) { #ifndef _WIN32_WCE /* do not report this as an error to allow the caller to resize */ errno=ERANGE; #endif /* _WIN32_WCE */ } else { _VALIDATE_RETURN( FALSE, EINVAL, 0); } /* unused but compiler can't tell */ return 0; } } /*** *_expandtime() - Expand the conversion specifier * *Purpose: * Expand the given strftime conversion specifier using the time struct * and store it in the supplied buffer. * * The expansion is locale-dependent. * * *** For internal use with strftime() only *** * *Entry: * char specifier = strftime conversion specifier to expand * const struct tm *tmptr = pointer to time/date structure * char **string = address of pointer to output string * size_t *count = address of char count (space in output area) * struct __lc_time_data *lc_time = pointer to locale-specific info * *Exit: * BOOL true for success, false for failure * *Exceptions: * *******************************************************************************/ static BOOL __cdecl _expandtime ( #ifndef _WIN32_WCE _locale_t plocinfo, #endif /* _WIN32_WCE */ char specifier, const struct tm *timeptr, char **string, size_t *left, struct __lc_time_data *lc_time, unsigned alternate_form ) { unsigned temp; /* temps */ int wdaytemp; /* Use a copy of the appropriate __lc_time_data pointer. This should prevent the necessity of locking/unlocking in mthread code (if we can guarantee that the various __lc_time data structures are always in the same segment). contents of time strings structure can now change, so thus we do use locking */ switch(specifier) { /* switch on specifier */ case('a'): /* abbreviated weekday name */ { _VALIDATE_RETURN( ( ( timeptr->tm_wday >=0 ) && ( timeptr->tm_wday <= 6 ) ), EINVAL, FALSE) _store_str((char *)(lc_time->wday_abbr[timeptr->tm_wday]), string, left); break; } case('A'): /* full weekday name */ { _VALIDATE_RETURN( ( ( timeptr->tm_wday >=0 ) && ( timeptr->tm_wday <= 6 ) ), EINVAL, FALSE) _store_str((char *)(lc_time->wday[timeptr->tm_wday]), string, left); break; } case('b'): /* abbreviated month name */ { _VALIDATE_RETURN( ( ( timeptr->tm_mon >=0 ) && ( timeptr->tm_mon <= 11 ) ), EINVAL, FALSE) _store_str((char *)(lc_time->month_abbr[timeptr->tm_mon]), string, left); break; } case('B'): /* full month name */ { _VALIDATE_RETURN( ( ( timeptr->tm_mon >=0 ) && ( timeptr->tm_mon <= 11 ) ), EINVAL, FALSE) _store_str((char *)(lc_time->month[timeptr->tm_mon]), string, left); break; } case('c'): /* date and time display */ if (alternate_form) { if(!_store_winword( #ifndef _WIN32_WCE plocinfo, #endif _WIN32_WCE WW_LDATEFMT, timeptr, string, left, lc_time)) { return FALSE; } if (*left == 0) return FALSE; *(*string)++=' '; (*left)--; if(!_store_winword( #ifndef _WIN32_WCE plocinfo, #endif _WIN32_WCE WW_TIMEFMT, timeptr, string, left, lc_time)) { return FALSE; } } else { if(!_store_winword( #ifndef _WIN32_WCE plocinfo, #endif _WIN32_WCE WW_SDATEFMT, timeptr, string, left, lc_time)) { return FALSE; } if (*left == 0) return FALSE; *(*string)++=' '; (*left)--; if(!_store_winword( #ifndef _WIN32_WCE plocinfo, #endif _WIN32_WCE WW_TIMEFMT, timeptr, string, left, lc_time)) { return FALSE; } } break; case('d'): /* mday in decimal (01-31) */ /* pass alternate_form as the no leading zeros flag */ { _VALIDATE_RETURN( ( ( timeptr->tm_mday >=1 ) && ( timeptr->tm_mday <= 31 ) ), EINVAL, FALSE) _store_num(timeptr->tm_mday, 2, string, left, alternate_form); break; } case('H'): /* 24-hour decimal (00-23) */ /* pass alternate_form as the no leading zeros flag */ { _VALIDATE_RETURN( ( ( timeptr->tm_hour >=0 ) && ( timeptr->tm_hour <= 23 ) ), EINVAL, FALSE) _store_num(timeptr->tm_hour, 2, string, left, alternate_form); break; } case('I'): /* 12-hour decimal (01-12) */ { _VALIDATE_RETURN( ( ( timeptr->tm_hour >=0 ) && ( timeptr->tm_hour <= 23 ) ), EINVAL, FALSE) if (!(temp = timeptr->tm_hour%12)) temp=12; /* pass alternate_form as the no leading zeros flag */ _store_num(temp, 2, string, left, alternate_form); break; } case('j'): /* yday in decimal (001-366) */ /* pass alternate_form as the no leading zeros flag */ { _VALIDATE_RETURN( ( ( timeptr->tm_yday >=0 ) && ( timeptr->tm_yday <= 365 ) ), EINVAL, FALSE) _store_num(timeptr->tm_yday+1, 3, string, left, alternate_form); break; } case('m'): /* month in decimal (01-12) */ /* pass alternate_form as the no leading zeros flag */ { _VALIDATE_RETURN( ( ( timeptr->tm_mon >=0 ) && ( timeptr->tm_mon <= 11 ) ), EINVAL, FALSE) _store_num(timeptr->tm_mon+1, 2, string, left, alternate_form); break; } case('M'): /* minute in decimal (00-59) */ /* pass alternate_form as the no leading zeros flag */ { _VALIDATE_RETURN( ( ( timeptr->tm_min >=0 ) && ( timeptr->tm_min <= 59 ) ), EINVAL, FALSE) _store_num(timeptr->tm_min, 2, string, left, alternate_form); break; } case('p'): /* AM/PM designation */ { _VALIDATE_RETURN( ( ( timeptr->tm_hour >=0 ) && ( timeptr->tm_hour <= 23 ) ), EINVAL, FALSE) if (timeptr->tm_hour <= 11) _store_str((char *)(lc_time->ampm[0]), string, left); else _store_str((char *)(lc_time->ampm[1]), string, left); break; } case('S'): /* secs in decimal (00-59) */ /* pass alternate_form as the no leading zeros flag */ { _VALIDATE_RETURN( ( ( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE) _store_num(timeptr->tm_sec, 2, string, left, alternate_form); break; } case('U'): /* sunday week number (00-53) */ _VALIDATE_RETURN( ( ( timeptr->tm_wday >=0 ) && ( timeptr->tm_wday <= 6 ) ), EINVAL, FALSE) wdaytemp = timeptr->tm_wday; goto weeknum; /* join common code */ case('w'): /* week day in decimal (0-6) */ /* pass alternate_form as the no leading zeros flag */ { _VALIDATE_RETURN( ( ( timeptr->tm_wday >=0 ) && ( timeptr->tm_wday <= 6 ) ), EINVAL, FALSE) _store_num(timeptr->tm_wday, 1, string, left, alternate_form); break; } case('W'): /* monday week number (00-53) */ _VALIDATE_RETURN( ( ( timeptr->tm_wday >=0 ) && ( timeptr->tm_wday <= 6 ) ), EINVAL, FALSE) if (timeptr->tm_wday == 0) /* monday based */ wdaytemp = 6; else wdaytemp = timeptr->tm_wday-1; weeknum: _VALIDATE_RETURN( ( ( timeptr->tm_yday >=0 ) && ( timeptr->tm_yday <= 365 ) ), EINVAL, FALSE) if (timeptr->tm_yday < wdaytemp) temp = 0; else { temp = timeptr->tm_yday/7; if ((timeptr->tm_yday%7) >= wdaytemp) temp++; } /* pass alternate_form as the no leading zeros flag */ _store_num(temp, 2, string, left, alternate_form); break; case('x'): /* date display */ if (alternate_form) { if(!_store_winword( #ifndef _WIN32_WCE plocinfo, #endif _WIN32_WCE WW_LDATEFMT, timeptr, string, left, lc_time)) { return FALSE; } } else { if(!_store_winword( #ifndef _WIN32_WCE plocinfo, #endif _WIN32_WCE WW_SDATEFMT, timeptr, string, left, lc_time)) { return FALSE; } } break; case('X'): /* time display */ if(!_store_winword( #ifndef _WIN32_WCE plocinfo, #endif _WIN32_WCE WW_TIMEFMT, timeptr, string, left, lc_time)) { return FALSE; } break; case('y'): /* year w/o century (00-99) */ { _VALIDATE_RETURN( ( timeptr->tm_year >=0 ), EINVAL, FALSE) temp = timeptr->tm_year%100; /* pass alternate_form as the no leading zeros flag */ _store_num(temp, 2, string, left, alternate_form); break; } case('Y'): /* year w/ century */ { _VALIDATE_RETURN( ( timeptr->tm_year >=0 ), EINVAL, FALSE) temp = (((timeptr->tm_year/100)+19)*100) + (timeptr->tm_year%100); /* pass alternate_form as the no leading zeros flag */ _store_num(temp, 4, string, left, alternate_form); break; } case('Z'): /* time zone name, if any */ case('z'): /* time zone name, if any */ #ifdef _POSIX_ tzset(); /* Set time zone info */ _store_str(tzname[((timeptr->tm_isdst)?1:0)], string, left); #else __tzset(); /* Set time zone info */ _BEGIN_SECURE_CRT_DEPRECATION_DISABLE _store_str(_tzname[((timeptr->tm_isdst)?1:0)], string, left); _END_SECURE_CRT_DEPRECATION_DISABLE #endif break; case('%'): /* percent sign */ *(*string)++ = '%'; (*left)--; break; default: /* unknown format directive */ /* ignore the directive and continue */ /* [ANSI: Behavior is undefined.] */ _ASSERTE( ( "Invalid format directive" , 0 ) ); return FALSE; break; } /* end % switch */ return TRUE; } /*** *_store_str() - Copy a time string * *Purpose: * Copy the supplied time string into the output string until * (1) we hit a null in the time string, or (2) the given count * goes to 0. * * *** For internal use with strftime() only *** * *Entry: * char *in = pointer to null terminated time string * char **out = address of pointer to output string * size_t *count = address of char count (space in output area) * *Exit: * none *Exceptions: * *******************************************************************************/ static void __cdecl _store_str ( char *in, char **out, size_t *count ) { while ((*count != 0) && (*in != '\0')) { *(*out)++ = *in++; (*count)--; } } /*** *_store_num() - Convert a number to ascii and copy it * *Purpose: * Convert the supplied number to decimal and store * in the output buffer. Update both the count and * buffer pointers. * * *** For internal use with strftime() only *** * *Entry: * int num = pointer to integer value * int digits = # of ascii digits to put into string * char **out = address of pointer to output string * size_t *count = address of char count (space in output area) * unsigned no_lead_zeros = flag indicating that padding by leading * zeros is not necessary * *Exit: * none *Exceptions: * *******************************************************************************/ static void __cdecl _store_num ( int num, int digits, char **out, size_t *count, unsigned no_lead_zeros ) { int temp = 0; if (no_lead_zeros) { _store_number (num, out, count); return; } if ((size_t)digits < *count) { for (digits--; (digits+1); digits--) { (*out)[digits] = (char)('0' + num % 10); num /= 10; temp++; } *out += temp; *count -= temp; } else *count = 0; } /*** *_store_number() - Convert positive integer to string * *Purpose: * Convert positive integer to a string and store it in the output * buffer with no null terminator. Update both the count and * buffer pointers. * * Differs from _store_num in that the precision is not specified, * and no leading zeros are added. * * *** For internal use with strftime() only *** * * Created from xtoi.c * *Entry: * int num = pointer to integer value * char **out = address of pointer to output string * size_t *count = address of char count (space in output area) * *Exit: * none * *Exceptions: * The buffer is filled until it is out of space. There is no * way to tell beforehand (as in _store_num) if the buffer will * run out of space. * *******************************************************************************/ static void __cdecl _store_number ( int num, char **out, size_t *count ) { char *p; /* pointer to traverse string */ char *firstdig; /* pointer to first digit */ char temp; /* temp char */ p = *out; /* put the digits in the buffer in reverse order */ if (*count > 1) { do { *p++ = (char) (num % 10 + '0'); (*count)--; } while ((num/=10) > 0 && *count > 1); } firstdig = *out; /* firstdig points to first digit */ *out = p; /* return pointer to next space */ p--; /* p points to last digit */ /* reverse the buffer */ do { temp = *p; *p-- = *firstdig; *firstdig++ = temp; /* swap *p and *firstdig */ } while (firstdig < p); /* repeat until halfway */ } /*** *_store_winword() - Store date/time in WinWord format * *Purpose: * Format the date/time in the supplied WinWord format * and store it in the supplied buffer. * * *** For internal use with strftime() only *** * * For simple localized Gregorian calendars (calendar type 1), the WinWord * format is converted token by token to strftime conversion specifiers. * _expandtime is then called to do the work. The WinWord format is * expected to be a character string (not wide-chars). * * For other calendar types, the Win32 APIs GetDateFormat/GetTimeFormat * are instead used to do all formatting, so that this routine doesn't * have to know about era/period strings, year offsets, etc. * * *Entry: * int field_code = code for ww_* field with format * const struct tm *tmptr = pointer to time/date structure * char **out = address of pointer to output string * size_t *count = address of char count (space in output area) * struct __lc_time_data *lc_time = pointer to locale-specific info * *Exit: * BOOL true for success, false for failure * *Exceptions: * *******************************************************************************/ static BOOL __cdecl _store_winword ( #ifndef _WIN32_WCE _locale_t plocinfo, #endif /* _WIN32_WCE */ int field_code, const struct tm *tmptr, char **out, size_t *count, struct __lc_time_data *lc_time ) { const char *format; char specifier; const char *p; int repeat; char *ampmstr; unsigned no_lead_zeros; #ifdef _WIN32_WCE size_t flen; LPWSTR wformat; size_t retval; #endif /* _WIN32_WCE */ switch (field_code) { case WW_SDATEFMT: format = lc_time->ww_sdatefmt; break; case WW_LDATEFMT: format = lc_time->ww_ldatefmt; break; case WW_TIMEFMT: default: format = lc_time->ww_timefmt; break; } if (lc_time->ww_caltype != 1) { /* We have something other than the basic Gregorian calendar */ SYSTEMTIME SystemTime; int cch; #ifndef _WIN32_WCE int (WINAPI * FormatFunc)(LCID, DWORD, const SYSTEMTIME *, LPCSTR, LPSTR, int); #else /* _WIN32_WCE */ int (WINAPI * FormatFunc)(LCID, DWORD, const SYSTEMTIME *, LPCWSTR, LPWSTR, int); #endif /* _WIN32_WCE */ if (field_code != WW_TIMEFMT) FormatFunc = GetDateFormat; else FormatFunc = GetTimeFormat; _VALIDATE_RETURN( ( tmptr->tm_year >= 0 ), EINVAL, FALSE ) // month 0 based _VALIDATE_RETURN( ( ( tmptr->tm_mon >= 0 ) && ( tmptr->tm_mon <= 11 ) ), EINVAL, FALSE ) // hour/min/sec 0 based _VALIDATE_RETURN( ( ( tmptr->tm_hour >= 0 ) && ( tmptr->tm_hour <= 23 ) ), EINVAL, FALSE ) _VALIDATE_RETURN( ( ( tmptr->tm_min >= 0 ) && ( tmptr->tm_min <= 59 ) ), EINVAL, FALSE ) _VALIDATE_RETURN( ( ( tmptr->tm_sec >= 0 ) && ( tmptr->tm_sec <= 59 ) ), EINVAL, FALSE ) // day 1 based _VALIDATE_RETURN( ( ( tmptr->tm_mday >= 1 ) && ( // Day is in valid range for the month ( ( _days[ tmptr->tm_mon + 1 ] - _days[ tmptr->tm_mon ] ) >= tmptr->tm_mday ) || // Special case for Feb in a leap year ( ( IS_LEAP_YEAR( tmptr->tm_year ) ) && ( tmptr->tm_mon == 2 ) && ( tmptr->tm_mday <= 29 ) ) ) ), EINVAL, FALSE ) SystemTime.wYear = (WORD)(tmptr->tm_year + 1900); SystemTime.wMonth = (WORD)(tmptr->tm_mon + 1); SystemTime.wDay = (WORD)(tmptr->tm_mday); SystemTime.wHour = (WORD)(tmptr->tm_hour); SystemTime.wMinute = (WORD)(tmptr->tm_min); SystemTime.wSecond = (WORD)(tmptr->tm_sec); SystemTime.wMilliseconds = 0; #ifdef _WIN32_WCE /* Convert format to Unicode */ flen = (strlen(format) + 1) * sizeof(wchar_t); wformat = (wchar_t *)_malloc_crt(flen); retval = 0; if (retval = mbstowcs(wformat, format, flen) == -1 || retval == flen) { _free_crt(wformat); wformat = NULL; } /* Find buffer size required */ cch = 0; if (wformat != NULL) { cch = FormatFunc(lc_time->ww_lcid, 0, &SystemTime, wformat, NULL, 0); } if (cch != 0) { char *buffer; LPWSTR wbuffer = (wchar_t *)_malloc_crt(cch * sizeof(wchar_t)); if (wbuffer != NULL) { /* Do actual date/time formatting */ cch = FormatFunc(lc_time->ww_lcid, 0, &SystemTime, wformat, wbuffer, cch); cch *= 2; /* each MBCS character could be two bytes */ buffer = (char *)_malloc_crt(cch); if ((buffer != NULL) && (cch = wcstombs(buffer, wbuffer, cch)) != -1) { /* Copy to output buffer */ p = buffer; while (--cch > 0 && *count > 0) { *(*out)++ = *p++; (*count)--; } } if (buffer != NULL) _free_crt(buffer); _free_crt(wbuffer); return TRUE; } } #else /* Find buffer size required */ cch = FormatFunc(lc_time->ww_lcid, 0, &SystemTime, format, NULL, 0); if (cch != 0) { char *buffer; /* Allocate buffer, first try stack, then heap */ buffer = (char *)_malloca(cch); if (buffer != NULL) { /* Do actual date/time formatting */ cch = FormatFunc(lc_time->ww_lcid, 0, &SystemTime, format, buffer, cch); /* Copy to output buffer */ p = buffer; while (--cch > 0 && *count > 0) { *(*out)++ = *p++; (*count)--; } _freea(buffer); return TRUE; } } #endif _WIN32_WCE /* In case of error, just fall through to localized Gregorian */ } while (*format && *count != 0) { specifier = 0; /* indicate no match */ no_lead_zeros = 0; /* default is print leading zeros */ /* count the number of repetitions of this character */ for (repeat=0, p=format; *p++ == *format; repeat++); /* leave p pointing to the beginning of the next token */ p--; /* switch on ascii format character and determine specifier */ switch (*format) { case 'M': switch (repeat) { case 1: no_lead_zeros = 1; /* fall thru */ case 2: specifier = 'm'; break; case 3: specifier = 'b'; break; case 4: specifier = 'B'; break; } break; case 'd': switch (repeat) { case 1: no_lead_zeros = 1; /* fall thru */ case 2: specifier = 'd'; break; case 3: specifier = 'a'; break; case 4: specifier = 'A'; break; } break; case 'y': switch (repeat) { case 2: specifier = 'y'; break; case 4: specifier = 'Y'; break; } break; case 'h': switch (repeat) { case 1: no_lead_zeros = 1; /* fall thru */ case 2: specifier = 'I'; break; } break; case 'H': switch (repeat) { case 1: no_lead_zeros = 1; /* fall thru */ case 2: specifier = 'H'; break; } break; case 'm': switch (repeat) { case 1: no_lead_zeros = 1; /* fall thru */ case 2: specifier = 'M'; break; } break; case 's': /* for compatibility; not strictly WinWord */ switch (repeat) { case 1: no_lead_zeros = 1; /* fall thru */ case 2: specifier = 'S'; break; } break; case 'A': case 'a': #ifndef _WIN32_WCE if (!__ascii_stricmp(format, "am/pm")) #else /* _WIN32_WCE */ if (!_stricmp(format, "am/pm")) #endif /* _WIN32_WCE */ p = format + 5; #ifndef _WIN32_WCE else if (!__ascii_stricmp(format, "a/p")) #else /* _WIN32_WCE */ else if (!_stricmp(format, "a/p")) #endif /* _WIN32_WCE */ p = format + 3; specifier = 'p'; break; case 't': /* t or tt time marker suffix */ if ( tmptr->tm_hour <= 11 ) ampmstr = lc_time->ampm[0]; else ampmstr = lc_time->ampm[1]; if ( (repeat == 1) && (*count > 0) ) { if ( _isleadbyte_l((int)*ampmstr, plocinfo) && (*count > 1) ) { /* catch \0 directly following leadbyte - invalid MBCS sequence, and do not copy to output */ if(ampmstr[1]=='\0') { _ASSERTE(("Invalid MBCS character sequence found in locale AMPM string",0)); return FALSE; } *(*out)++ = *ampmstr++; (*count)--; } *(*out)++ = *ampmstr++; (*count)--; } else { while (*ampmstr != 0 && *count > 0) { if (_isleadbyte_l((int)*ampmstr, plocinfo) && *count > 1) { /* handle dud string leadbyte, EOS */ if(ampmstr[1]=='\0') { _ASSERTE(("Invalid MBCS character sequence found in locale AMPM string",0)); return FALSE; } *(*out)++ = *ampmstr++; (*count)--; } *(*out)++ = *ampmstr++; (*count)--; } } format = p; continue; case '\'': /* literal string */ if (repeat & 1) /* odd number */ { format += repeat; while (*format && *count != 0) { if (*format == '\'') { format++; break; } if ( _isleadbyte_l((int)*format, plocinfo) && (*count > 1) ) { /* catch \0 directly following leadbyte - invalid MBCS sequence, and do not copy to output */ if(format[1]=='\0') { _ASSERTE(("Invalid MBCS character sequence passed into strftime",0)); return FALSE; } *(*out)++ = *format++; (*count)--; } *(*out)++ = *format++; (*count)--; } } else { /* even number */ format += repeat; } continue; default: /* non-control char, print it */ break; } /* switch */ /* expand specifier, or copy literal if specifier not found */ if (specifier) { if (!_expandtime( #ifndef _WIN32_WCE plocinfo, #endif /* _WIN32_WCE */ specifier, tmptr, out, count, lc_time, no_lead_zeros)) { return FALSE; } format = p; /* bump format up to the next token */ } else { if ( _isleadbyte_l((int)*format, plocinfo) && (*count > 1)) { /* catch \0 directly following leadbyte - invalid MBCS sequence, and do not copy to output */ if(format[1]=='\0') { _ASSERTE(("Invalid MBCS character sequence passed into strftime",0)); return FALSE; } *(*out)++ = *format++; (*count)--; } *(*out)++ = *format++; (*count)--; } } /* while */ return TRUE; }