diff options
Diffstat (limited to 'private/crt32/time/strftime.c')
-rw-r--r-- | private/crt32/time/strftime.c | 891 |
1 files changed, 891 insertions, 0 deletions
diff --git a/private/crt32/time/strftime.c b/private/crt32/time/strftime.c new file mode 100644 index 000000000..516f0f88e --- /dev/null +++ b/private/crt32/time/strftime.c @@ -0,0 +1,891 @@ +/*** +*strftime.c - String Format Time +* +* Copyright (c) 1988-1993, 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 +* <cruntime.h>, removed #include <register.h> 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-14-93 CFW Disable _alternate_form for 'X' specifier, fix count bug. +* 07-15-93 GJF Call __tzset() in place of _tzset(). +* +*******************************************************************************/ + +#include <cruntime.h> +#include <internal.h> +#include <os2dll.h> +#include <time.h> +#include <locale.h> +#include <setlocal.h> +#ifdef _INTL +#include <ctype.h> +#include <string.h> +#endif + +/* Prototypes for local routines */ +static void _CRTAPI3 _expandtime (char specifier, const struct tm *tmptr, char **out, size_t *count); +static void _CRTAPI3 _store_str (char *in, char **out, size_t *count); +static void _CRTAPI3 _store_num (int num, int digits, char **out, size_t *count); +#ifndef _INTL +static void _CRTAPI3 _store_time (const struct tm *tmptr, char **out, size_t *count); +static void _CRTAPI3 _store_date (const struct tm *tmptr, char **out, size_t *count); +#else /* _INTL */ +static void _CRTAPI3 _store_number (int num, char **out, size_t *count); +static void _CRTAPI3 _store_winword (const char *format, const struct tm *tmptr, char **out, size_t *count); +#endif + + +/* LC_TIME data for local "C" */ + +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"} + +#ifdef _INTL + , { "M/d/yy" } + , { "dddd, MMMM dd, yyyy" } + , { "H:mm:ss" } +#endif + }; + + +/* Pointer to the current LC_TIME data structure. */ + +struct _lc_time_data *_lc_time_curr = &_lc_time_c; + +/* Flags */ +unsigned _alternate_form = 0; +#ifdef _INTL +unsigned _no_lead_zeros = 0; +#endif + + +/*** +*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: +* +*******************************************************************************/ + +size_t _CRTAPI1 strftime ( + char *string, + size_t maxsize, + const char *format, + const struct tm *timeptr + ) +{ + + size_t left; /* space left in output string */ + + + /* Copy maxsize into temp. */ + left = maxsize; + + _mlock (_LC_TIME_LOCK); + + /* 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. */ + + format++; /* skip over % char */ + +#ifdef _INTL + /* process flags */ + _alternate_form = 0; + if (*format == '#') + { + _alternate_form = 1; + format++; + } +#endif + _expandtime(*format, timeptr, &string, &left); + format++; /* skip format char */ + break; + + + default: + + /* store character, bump pointers, dec the char count */ +#ifdef _INTL + if (isleadbyte((int)(*format)) && left > 1) + { + *string++ = *format++; + left--; + } +#endif + *string++ = *format++; + left--; + break; + } + } + + + /* All done. See if we terminated because we hit a null char or because + we ran out of space */ + + done: + + _munlock (_LC_TIME_LOCK); + + if (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 + 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) +* +*Exit: +* none +* +*Exceptions: +* +*******************************************************************************/ + +static void _CRTAPI3 _expandtime ( + char specifier, + const struct tm *timeptr, + char **string, + size_t *left + ) +{ + struct _lc_time_data *lc_time; /* lc_time data pointer */ + unsigned temp; /* temps */ + int wdaytemp; + + /* Get a copy of the current _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). */ + /* _INTL: contents of time strings structure can now change, + so thus we do use locking */ + + lc_time = _lc_time_curr; + + switch(specifier) { /* switch on specifier */ + + case('a'): /* abbreviated weekday name */ + _store_str((char *)(lc_time->wday_abbr[timeptr->tm_wday]), + string, left); + break; + + case('A'): /* full weekday name */ + _store_str((char *)(lc_time->wday[timeptr->tm_wday]), + string, left); + break; + + case('b'): /* abbreviated month name */ + _store_str((char *)(lc_time->month_abbr[timeptr->tm_mon]), + string, left); + break; + + case('B'): /* full month name */ + _store_str((char *)(lc_time->month[timeptr->tm_mon]), + string, left); + break; + + case('c'): /* date and time display */ +#ifdef _INTL + if (_alternate_form) + { + _alternate_form = FALSE; + _store_winword(lc_time->ww_ldatefmt, timeptr, string, left); + if (*left == 0) + return; + *(*string)++=' '; + (*left)--; + _store_winword(lc_time->ww_timefmt, timeptr, string, left); + } + else { + _store_winword(lc_time->ww_sdatefmt, timeptr, string, left); + if (*left == 0) + return; + *(*string)++=' '; + (*left)--; + _store_winword(lc_time->ww_timefmt, timeptr, string, left); + } +#else + if ((_DATE_LENGTH+_TIME_LENGTH+1) < *left) + { + _store_date(timeptr, string, left); + *(*string)++=' '; + (*left)--; + _store_time(timeptr, string, left); + } + else + *left=0; +#endif /* _INTL */ + break; + + case('d'): /* mday in decimal (01-31) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + _store_num(timeptr->tm_mday, 2, string, left); + break; + + case('H'): /* 24-hour decimal (00-23) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + _store_num(timeptr->tm_hour, 2, string, left); + break; + + case('I'): /* 12-hour decimal (01-12) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + if (!(temp = timeptr->tm_hour%12)) + temp=12; + _store_num(temp, 2, string, left); + break; + + case('j'): /* yday in decimal (001-366) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + _store_num(timeptr->tm_yday+1, 3, string, left); + break; + + case('m'): /* month in decimal (01-12) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + _store_num(timeptr->tm_mon+1, 2, string, left); + break; + + case('M'): /* minute in decimal (00-59) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + _store_num(timeptr->tm_min, 2, string, left); + break; + + case('p'): /* AM/PM designation */ + 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) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + _store_num(timeptr->tm_sec, 2, string, left); + break; + + case('U'): /* sunday week number (00-53) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + wdaytemp = timeptr->tm_wday; + goto weeknum; /* join common code */ + + case('w'): /* week day in decimal (0-6) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + _store_num(timeptr->tm_wday, 1, string, left); + break; + + case('W'): /* monday week number (00-53) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + if (timeptr->tm_wday == 0) /* monday based */ + wdaytemp = 6; + else + wdaytemp = timeptr->tm_wday-1; + weeknum: + if (timeptr->tm_yday < wdaytemp) + temp=0; + else { + temp = timeptr->tm_yday/7; + if ((timeptr->tm_yday%7) >= wdaytemp) + temp++; + } + _store_num(temp, 2, string, left); + break; + + case('x'): /* date display */ +#ifdef _INTL + if (_alternate_form) + { + _alternate_form = FALSE; + _store_winword(lc_time->ww_ldatefmt, timeptr, string, left); + } + else + { + _store_winword(lc_time->ww_sdatefmt, timeptr, string, left); + } +#else + _store_date(timeptr, string, left); +#endif + break; + + case('X'): /* time display */ +#ifdef _INTL + _alternate_form = FALSE; + _store_winword(lc_time->ww_timefmt, timeptr, string, left); +#else + _store_time(timeptr, string, left); +#endif + break; + + case('y'): /* year w/o century (00-99) */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + temp = timeptr->tm_year%100; + _store_num(temp, 2, string, left); + break; + + case('Y'): /* year w/ century */ +#ifdef _INTL + _no_lead_zeros = _alternate_form; +#endif + temp = (((timeptr->tm_year/100)+19)*100) + + (timeptr->tm_year%100); + _store_num(temp, 4, string, left); + 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 */ + _store_str(_tzname[((timeptr->tm_isdst)?1:0)], + string, left); +#endif + break; + + case('%'): /* percent sign */ + *(*string)++ = '%'; + (*left)--; + break; + + default: /* unknown format directive */ + /* ignore the directive and continue */ + /* [ANSI: Behavior is undefined.] */ + break; + + } /* end % switch */ +} + + +/*** +*_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 _CRTAPI3 _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) +* +*Exit: +* none +*Exceptions: +* +*******************************************************************************/ + +static void _CRTAPI3 _store_num ( + int num, + int digits, + char **out, + size_t *count + ) +{ +int temp=0; + +#ifdef _INTL + if (_no_lead_zeros) { + _store_number (num, out, count); + return; + } +#endif + +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; +} + + +#ifndef _INTL + +/*** +*_store_time() - Store time in appropriate format +* +*Purpose: +* Format the time in the current locale's format +* and store it in the supplied buffer. +* +* [*** Currently, this routine assumes standard "C" +* locale. When full localization support is introduced, +* this functionality will have to be expanded.] +* +* Standard "C" locale time format: +* +* hh:mm:ss +* +* *** For internal use with strftime() only *** +* +*Entry: +* +* 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) +* +*Exit: +* none +*Exceptions: +* +*******************************************************************************/ + +static void _CRTAPI3 _store_time ( + const struct tm *tmptr, + char **out, + size_t *count + ) +{ + +if (_TIME_LENGTH < *count) { + + _store_num(tmptr->tm_hour, 2, out, count); + *(*out)++ = ':'; + _store_num(tmptr->tm_min, 2, out, count); + *(*out)++ = ':'; + _store_num(tmptr->tm_sec, 2, out, count); + + *count -= 2; /* count the colons */ +} + +else + *count = 0; + +} + + +/*** +*_store_date() - Store date in appropriate format +* +*Purpose: +* Format the date in the current locale's format +* and store it in the supplied buffer. +* +* [*** Currently, this routine assumes standard "C" +* locale. When full localization support is introduced, +* this functionality will have to be expanded.] +* +* Standard "C" locale date format: +* +* mm/dd/yy +* +* *** For internal use with strftime() only *** +* +*Entry: +* +* 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) +* +*Exit: +* none +*Exceptions: +* +*******************************************************************************/ + +static void _CRTAPI3 _store_date ( + const struct tm *tmptr, + char **out, + size_t *count + ) +{ + +if (_DATE_LENGTH < *count) { + + _store_num(tmptr->tm_mon+1, 2, out, count); + *(*out)++ = '/'; + _store_num(tmptr->tm_mday, 2, out, count); + *(*out)++ = '/'; + _store_num(tmptr->tm_year%100, 2, out, count); + + *count -= 2; /* count the backslashes */ + } + +else + *count = 0; + +} + +#else /* _INTL */ + +/*** +*_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 _CRTAPI3 _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 *** +* +* 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). +* +*Entry: +* const char **format = address of pointer to WinWord 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) +* +*Exit: +* none +* +*Exceptions: +* +*******************************************************************************/ + +static void _CRTAPI3 _store_winword ( + const char *format, + const struct tm *tmptr, + char **out, + size_t *count + ) +{ + char specifier; + const char *p; + int repeat; + + 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': + if (!_stricmp(format, "am/pm")) + p = format + 5; + else if (!_stricmp(format, "a/p")) + p = format + 3; + specifier = 'p'; + break; + case '\'': /* literal string */ + if (repeat & 1) /* odd number */ + { + format += repeat; + while (*format && *count != 0) + { + if (*format == '\'') + { + format++; + break; + } + if (isleadbyte((int)*format)) + { + *(*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) + { + _expandtime(specifier, tmptr, out, count); + format = p; /* bump format up to the next token */ + } else { + if (isleadbyte((int)*format)) + { + *(*out)++ = *format++; + (*count)--; + } + *(*out)++ = *format++; + (*count)--; + } + } /* while */ +} + +#endif /* _INTL */ |