home *** CD-ROM | disk | FTP | other *** search
- /***
- *strftime.c - String Format Time
- *
- * Copyright (c) 1988-1997, Microsoft Corporation. All rights reserved.
- *
- *Purpose:
- *
- *******************************************************************************/
-
- #ifdef _MAC
- #define _WLM_NOFORCE_LIBS
- #include <windows.h>
- #ifdef _WIN32
- #undef _WIN32 /* windows.h should NOT set _WIN32 */
- #endif /* _WIN32 */
- /* The following two TYPEDEFs are NOT defined in <windows.h> for the Mac */
- typedef DWORD LCTYPE; /* from windows.h */
- typedef int mbstate_t; /* from wchar.h */
- #endif /* _MAC */
-
- #include <cruntime.h>
- #include <internal.h>
- #include <mtdll.h>
- #include <time.h>
- #include <locale.h>
- #include <setlocal.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <string.h>
- #include <xlocinfo.h>
-
- /* Prototypes for local routines */
- static void __cdecl _expandtime (char specifier, const struct tm *tmptr,
- char **out, size_t *count, struct __lc_time_data *lc_time);
- 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);
- static void __cdecl _store_number (int num, char **out, size_t *count);
- static void __cdecl _store_winword (const char *format, const struct tm *tmptr, char **out, size_t *count, struct __lc_time_data *lc_time);
-
-
- /* LC_TIME data for local "C" */
-
- __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"}
-
- , { "M/d/yy" }
- , { "dddd, MMMM dd, yyyy" }
- , { "H:mm:ss" }
- };
-
- /* Pointer to the current LC_TIME data structure. */
-
- struct __lc_time_data *__lc_time_curr = &__lc_time_c;
-
- /* Flags */
- unsigned __alternate_form;
- unsigned __no_lead_zeros;
-
- #define TIME_SEP ':'
-
- /* get a copy of the current day names */
- char * __cdecl _Getdays (
- void
- )
- {
- const struct __lc_time_data *pt = __lc_time_curr;
- size_t n, len = 0;
- char *p;
-
- for (n = 0; n < 7; ++n)
- len += strlen(pt->wday_abbr[n]) + strlen(pt->wday[n]) + 2;
- p = (char *)malloc(len + 1);
-
- if (p != 0) {
- char *s = p;
-
- for (n = 0; n < 7; ++n) {
- *s++ = TIME_SEP;
- s += strlen(strcpy(s, pt->wday_abbr[n]));
- *s++ = TIME_SEP;
- s += strlen(strcpy(s, pt->wday[n]));
- }
- *s++ = '\0';
- }
-
- return (p);
- }
-
- /* get a copy of the current month names */
- char * __cdecl _Getmonths (
- void
- )
- {
- const struct __lc_time_data *pt = __lc_time_curr;
- size_t n, len = 0;
- char *p;
-
- for (n = 0; n < 12; ++n)
- len += strlen(pt->month_abbr[n]) + strlen(pt->month[n]) + 2;
- p = (char *)malloc(len + 1);
-
- if (p != 0) {
- char *s = p;
-
- for (n = 0; n < 12; ++n) {
- *s++ = TIME_SEP;
- s += strlen(strcpy(s, pt->month_abbr[n]));
- *s++ = TIME_SEP;
- s += strlen(strcpy(s, pt->month[n]));
- }
- *s++ = '\0';
- }
-
- return (p);
- }
-
- /* get a copy of the current time locale information */
- void * __cdecl _Gettnames (
- void
- )
- {
- const struct __lc_time_data *pt = __lc_time_curr;
- size_t n, len = 0;
- void *p;
-
- 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;
- p = malloc(sizeof (*pt) + len);
-
- if (p != 0)
- {struct __lc_time_data *pn = (struct __lc_time_data *)p;
- char *s = (char *)p + sizeof (*pt);
-
- memcpy(p, __lc_time_curr, sizeof (*pt));
- for (n = 0; n < 7; ++n)
- {pn->wday_abbr[n] = s;
- s += strlen(strcpy(s, pt->wday_abbr[n])) + 1;
- pn->wday[n] = s;
- s += strlen(strcpy(s, pt->wday[n])) + 1; }
- for (n = 0; n < 12; ++n)
- {pn->month_abbr[n] = s;
- s += strlen(strcpy(s, pt->month_abbr[n])) + 1;
- pn->month[n] = s;
- s += strlen(strcpy(s, pt->month[n])) + 1; }
- pn->ampm[0] = s;
- s += strlen(strcpy(s, pt->ampm[0])) + 1;
- pn->ampm[1] = s;
- s += strlen(strcpy(s, pt->ampm[1])) + 1;
- pn->ww_sdatefmt = s;
- s += strlen(strcpy(s, pt->ww_sdatefmt)) + 1;
- pn->ww_ldatefmt = s;
- s += strlen(strcpy(s, pt->ww_ldatefmt)) + 1;
- pn->ww_timefmt = s; }
-
- return (p);
- }
-
-
- /***
- *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 __cdecl strftime (
- char *string,
- size_t maxsize,
- const char *format,
- const struct tm *timeptr
- )
- {
- return (_Strftime(string, maxsize, format, timeptr, 0));
- }
-
- /***
- *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:
- *
- *******************************************************************************/
-
- size_t __cdecl _Strftime (
- char *string,
- size_t maxsize,
- const char *format,
- const struct tm *timeptr,
- void *lc_time_arg
- )
- {
- struct __lc_time_data *lc_time = lc_time_arg == 0 ? __lc_time_curr
- : (struct __lc_time_data *)lc_time_arg;
-
- size_t left; /* space left in output string */
- #ifdef _MT
- int local_lock_flag;
- #endif /* _MT */
-
- /* Copy maxsize into temp. */
- left = maxsize;
-
- _lock_locale( local_lock_flag )
-
- /* 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 */
-
- /* process flags */
- __alternate_form = 0;
- if (*format == '#')
- {
- __alternate_form = 1;
- format++;
- }
- _expandtime(*format, timeptr, &string, &left,
- lc_time);
- format++; /* skip format char */
- break;
-
-
- default:
-
- /* store character, bump pointers, dec the char count */
- if (isleadbyte((int)(*format)) && left > 1)
- {
- *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:
-
- _unlock_locale( local_lock_flag )
-
- 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)
- * struct __lc_time_data *lc_time = pointer to locale-specific info
- *
- *Exit:
- * none
- *
- *Exceptions:
- *
- *******************************************************************************/
-
- static void __cdecl _expandtime (
- char specifier,
- const struct tm *timeptr,
- char **string,
- size_t *left,
- struct __lc_time_data *lc_time
- )
- {
- 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 */
- _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 */
- if (__alternate_form)
- {
- __alternate_form = FALSE;
- _store_winword(lc_time->ww_ldatefmt, timeptr, string, left, lc_time);
- if (*left == 0)
- return;
- *(*string)++=' ';
- (*left)--;
- _store_winword(lc_time->ww_timefmt, timeptr, string, left, lc_time);
- }
- else {
- _store_winword(lc_time->ww_sdatefmt, timeptr, string, left, lc_time);
- if (*left == 0)
- return;
- *(*string)++=' ';
- (*left)--;
- _store_winword(lc_time->ww_timefmt, timeptr, string, left, lc_time);
- }
- break;
-
- case('d'): /* mday in decimal (01-31) */
- __no_lead_zeros = __alternate_form;
- _store_num(timeptr->tm_mday, 2, string, left);
- break;
-
- case('H'): /* 24-hour decimal (00-23) */
- __no_lead_zeros = __alternate_form;
- _store_num(timeptr->tm_hour, 2, string, left);
- break;
-
- case('I'): /* 12-hour decimal (01-12) */
- __no_lead_zeros = __alternate_form;
- if (!(temp = timeptr->tm_hour%12))
- temp=12;
- _store_num(temp, 2, string, left);
- break;
-
- case('j'): /* yday in decimal (001-366) */
- __no_lead_zeros = __alternate_form;
- _store_num(timeptr->tm_yday+1, 3, string, left);
- break;
-
- case('m'): /* month in decimal (01-12) */
- __no_lead_zeros = __alternate_form;
- _store_num(timeptr->tm_mon+1, 2, string, left);
- break;
-
- case('M'): /* minute in decimal (00-59) */
- __no_lead_zeros = __alternate_form;
- _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) */
- __no_lead_zeros = __alternate_form;
- _store_num(timeptr->tm_sec, 2, string, left);
- break;
-
- case('U'): /* sunday week number (00-53) */
- __no_lead_zeros = __alternate_form;
- wdaytemp = timeptr->tm_wday;
- goto weeknum; /* join common code */
-
- case('w'): /* week day in decimal (0-6) */
- __no_lead_zeros = __alternate_form;
- _store_num(timeptr->tm_wday, 1, string, left);
- break;
-
- case('W'): /* monday week number (00-53) */
- __no_lead_zeros = __alternate_form;
- 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 */
- if (__alternate_form)
- {
- __alternate_form = FALSE;
- _store_winword(lc_time->ww_ldatefmt, timeptr, string, left, lc_time);
- }
- else
- {
- _store_winword(lc_time->ww_sdatefmt, timeptr, string, left, lc_time);
- }
- break;
-
- case('X'): /* time display */
- __alternate_form = FALSE;
- _store_winword(lc_time->ww_timefmt, timeptr, string, left, lc_time);
- break;
-
- case('y'): /* year w/o century (00-99) */
- __no_lead_zeros = __alternate_form;
- temp = timeptr->tm_year%100;
- _store_num(temp, 2, string, left);
- break;
-
- case('Y'): /* year w/ century */
- __no_lead_zeros = __alternate_form;
- 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 _MAC
- _tzset(); /* Set time zone info */
- #else /* _MAC */
- __tzset(); /* Set time zone info */
- #endif /* _MAC */
- _store_str(_tzname[((timeptr->tm_isdst)?1:0)],
- string, left);
- 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 __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)
- *
- *Exit:
- * none
- *Exceptions:
- *
- *******************************************************************************/
-
- static void __cdecl _store_num (
- int num,
- int digits,
- char **out,
- size_t *count
- )
- {
- 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 ***
- *
- * 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)
- * struct __lc_time_data *lc_time = pointer to locale-specific info
- *
- *Exit:
- * none
- *
- *Exceptions:
- *
- *******************************************************************************/
-
- static void __cdecl _store_winword (
- const char *format,
- const struct tm *tmptr,
- char **out,
- size_t *count,
- struct __lc_time_data *lc_time
- )
- {
- char specifier;
- const char *p;
- int repeat;
- char *ampmstr;
-
- 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 't': /* t or tt time marker suffix */
- if ( tmptr->tm_hour <= 11 )
- ampmstr = lc_time->ampm[0];
- else
- ampmstr = lc_time->ampm[1];
-
- while ( (repeat > 0) && (*count > 0) )
- {
- if ( isleadbyte((int)*ampmstr) &&
- (*count > 1) )
- {
- *(*out)++ = *ampmstr++;
- (*count)--;
- }
- *(*out)++ = *ampmstr++;
- (*count)--;
- repeat--;
- }
- format = p;
- continue;
-
- case '\'': /* literal string */
- if (repeat & 1) /* odd number */
- {
- format += repeat;
- while (*format && *count != 0)
- {
- if (*format == '\'')
- {
- format++;
- break;
- }
- if ( isleadbyte((int)*format) &&
- (*count > 1) )
- {
- *(*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, lc_time);
- format = p; /* bump format up to the next token */
- } else {
- if (isleadbyte((int)*format))
- {
- *(*out)++ = *format++;
- (*count)--;
- }
- *(*out)++ = *format++;
- (*count)--;
- }
- } /* while */
- }
-
-