home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / strftime.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  32KB  |  877 lines

  1. /***
  2. *strftime.c - String Format Time
  3. *
  4. *       Copyright (c) 1988-1997, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *
  8. *******************************************************************************/
  9.  
  10. #ifdef _MAC
  11. #define _WLM_NOFORCE_LIBS
  12. #include <windows.h>
  13. #ifdef _WIN32
  14. #undef  _WIN32  /* windows.h should NOT set _WIN32 */
  15. #endif  /* _WIN32 */
  16. /* The following two TYPEDEFs are NOT defined in <windows.h> for the Mac */
  17. typedef DWORD LCTYPE;           /* from windows.h */
  18. typedef int mbstate_t;          /* from wchar.h */
  19. #endif  /* _MAC */
  20.  
  21. #include <cruntime.h>
  22. #include <internal.h>
  23. #include <mtdll.h>
  24. #include <time.h>
  25. #include <locale.h>
  26. #include <setlocal.h>
  27. #include <ctype.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <xlocinfo.h>
  31.  
  32. /* Prototypes for local routines */
  33. static void __cdecl _expandtime (char specifier, const struct tm *tmptr,
  34.         char **out, size_t *count, struct __lc_time_data *lc_time);
  35. static void __cdecl _store_str (char *in, char **out, size_t *count);
  36. static void __cdecl _store_num (int num, int digits, char **out, size_t *count);
  37. static void __cdecl _store_number (int num, char **out, size_t *count);
  38. static void __cdecl _store_winword (const char *format, const struct tm *tmptr, char **out, size_t *count, struct __lc_time_data *lc_time);
  39.  
  40.  
  41. /* LC_TIME data for local "C" */
  42.  
  43. __declspec(selectany) struct __lc_time_data __lc_time_c = {
  44.  
  45.         {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"},
  46.  
  47.         {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  48.                 "Friday", "Saturday", },
  49.  
  50.         {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
  51.                 "Sep", "Oct", "Nov", "Dec"},
  52.  
  53.         {"January", "February", "March", "April", "May", "June",
  54.                 "July", "August", "September", "October",
  55.                 "November", "December"},
  56.  
  57.         {"AM", "PM"}
  58.  
  59.         , { "M/d/yy" }
  60.         , { "dddd, MMMM dd, yyyy" }
  61.         , { "H:mm:ss" }
  62.         };
  63.  
  64. /* Pointer to the current LC_TIME data structure. */
  65.  
  66. struct __lc_time_data *__lc_time_curr = &__lc_time_c;
  67.  
  68. /* Flags */
  69. unsigned __alternate_form;
  70. unsigned __no_lead_zeros;
  71.  
  72. #define TIME_SEP        ':'
  73.  
  74. /*      get a copy of the current day names */
  75. char * __cdecl _Getdays (
  76.         void
  77.         )
  78. {
  79.         const struct __lc_time_data *pt = __lc_time_curr;
  80.         size_t n, len = 0;
  81.         char *p;
  82.  
  83.         for (n = 0; n < 7; ++n)
  84.                 len += strlen(pt->wday_abbr[n]) + strlen(pt->wday[n]) + 2;
  85.         p = (char *)malloc(len + 1);
  86.  
  87.         if (p != 0) {
  88.                 char *s = p;
  89.  
  90.                 for (n = 0; n < 7; ++n) {
  91.                         *s++ = TIME_SEP;
  92.                         s += strlen(strcpy(s, pt->wday_abbr[n]));
  93.                         *s++ = TIME_SEP;
  94.                         s += strlen(strcpy(s, pt->wday[n]));
  95.                 }
  96.                 *s++ = '\0';
  97.         }
  98.  
  99.         return (p);
  100. }
  101.  
  102. /*      get a copy of the current month names */
  103. char * __cdecl _Getmonths (
  104.         void
  105.         )
  106. {
  107.         const struct __lc_time_data *pt = __lc_time_curr;
  108.         size_t n, len = 0;
  109.         char *p;
  110.  
  111.         for (n = 0; n < 12; ++n)
  112.                 len += strlen(pt->month_abbr[n]) + strlen(pt->month[n]) + 2;
  113.         p = (char *)malloc(len + 1);
  114.  
  115.         if (p != 0) {
  116.                 char *s = p;
  117.  
  118.                 for (n = 0; n < 12; ++n) {
  119.                         *s++ = TIME_SEP;
  120.                         s += strlen(strcpy(s, pt->month_abbr[n]));
  121.                         *s++ = TIME_SEP;
  122.                         s += strlen(strcpy(s, pt->month[n]));
  123.                 }
  124.                 *s++ = '\0';
  125.         }
  126.  
  127.         return (p);
  128. }
  129.  
  130. /*      get a copy of the current time locale information */
  131. void * __cdecl _Gettnames (
  132.         void
  133.         )
  134. {
  135.         const struct __lc_time_data *pt = __lc_time_curr;
  136.         size_t n, len = 0;
  137.         void *p;
  138.  
  139.         for (n = 0; n < 7; ++n)
  140.                 len += strlen(pt->wday_abbr[n]) + strlen(pt->wday[n]) + 2;
  141.         for (n = 0; n < 12; ++n)
  142.                 len += strlen(pt->month_abbr[n]) + strlen(pt->month[n]) + 2;
  143.         len += strlen(pt->ampm[0]) + strlen(pt->ampm[1]) + 2;
  144.         len += strlen(pt->ww_sdatefmt) + 1;
  145.         len += strlen(pt->ww_ldatefmt) + 1;
  146.         len += strlen(pt->ww_timefmt) + 1;
  147.         p = malloc(sizeof (*pt) + len);
  148.  
  149.         if (p != 0)
  150.                 {struct __lc_time_data *pn = (struct __lc_time_data *)p;
  151.                 char *s = (char *)p + sizeof (*pt);
  152.  
  153.                 memcpy(p, __lc_time_curr, sizeof (*pt));
  154.                 for (n = 0; n < 7; ++n)
  155.                         {pn->wday_abbr[n] = s;
  156.                         s += strlen(strcpy(s, pt->wday_abbr[n])) + 1;
  157.                         pn->wday[n] = s;
  158.                         s += strlen(strcpy(s, pt->wday[n])) + 1; }
  159.                 for (n = 0; n < 12; ++n)
  160.                         {pn->month_abbr[n] = s;
  161.                         s += strlen(strcpy(s, pt->month_abbr[n])) + 1;
  162.                         pn->month[n] = s;
  163.                         s += strlen(strcpy(s, pt->month[n])) + 1; }
  164.                 pn->ampm[0] = s;
  165.                 s += strlen(strcpy(s, pt->ampm[0])) + 1;
  166.                 pn->ampm[1] = s;
  167.                 s += strlen(strcpy(s, pt->ampm[1])) + 1;
  168.                 pn->ww_sdatefmt = s;
  169.                 s += strlen(strcpy(s, pt->ww_sdatefmt)) + 1;
  170.                 pn->ww_ldatefmt = s;
  171.                 s += strlen(strcpy(s, pt->ww_ldatefmt)) + 1;
  172.                 pn->ww_timefmt = s; }
  173.  
  174.         return (p);
  175. }
  176.  
  177.  
  178. /***
  179. *size_t strftime(string, maxsize, format, timeptr) - Format a time string
  180. *
  181. *Purpose:
  182. *       Place characters into the user's output buffer expanding time
  183. *       format directives as described in the user's control string.
  184. *       Use the supplied 'tm' structure for time data when expanding
  185. *       the format directives.
  186. *       [ANSI]
  187. *
  188. *Entry:
  189. *       char *string = pointer to output string
  190. *       size_t maxsize = max length of string
  191. *       const char *format = format control string
  192. *       const struct tm *timeptr = pointer to tb data structure
  193. *
  194. *Exit:
  195. *       !0 = If the total number of resulting characters including the
  196. *       terminating null is not more than 'maxsize', then return the
  197. *       number of chars placed in the 'string' array (not including the
  198. *       null terminator).
  199. *
  200. *       0 = Otherwise, return 0 and the contents of the string are
  201. *       indeterminate.
  202. *
  203. *Exceptions:
  204. *
  205. *******************************************************************************/
  206.  
  207. size_t __cdecl strftime (
  208.         char *string,
  209.         size_t maxsize,
  210.         const char *format,
  211.         const struct tm *timeptr
  212.         )
  213. {
  214.         return (_Strftime(string, maxsize, format, timeptr, 0));
  215. }
  216.  
  217. /***
  218. *size_t _Strftime(string, maxsize, format,
  219. *       timeptr, lc_time) - Format a time string for a given locale
  220. *
  221. *Purpose:
  222. *       Place characters into the user's output buffer expanding time
  223. *       format directives as described in the user's control string.
  224. *       Use the supplied 'tm' structure for time data when expanding
  225. *       the format directives. use the locale information at lc_time.
  226. *       [ANSI]
  227. *
  228. *Entry:
  229. *       char *string = pointer to output string
  230. *       size_t maxsize = max length of string
  231. *       const char *format = format control string
  232. *       const struct tm *timeptr = pointer to tb data structure
  233. *               struct __lc_time_data *lc_time = pointer to locale-specific info
  234. *                       (passed as void * to avoid type mismatch with C++)
  235. *
  236. *Exit:
  237. *       !0 = If the total number of resulting characters including the
  238. *       terminating null is not more than 'maxsize', then return the
  239. *       number of chars placed in the 'string' array (not including the
  240. *       null terminator).
  241. *
  242. *       0 = Otherwise, return 0 and the contents of the string are
  243. *       indeterminate.
  244. *
  245. *Exceptions:
  246. *
  247. *******************************************************************************/
  248.  
  249. size_t __cdecl _Strftime (
  250.         char *string,
  251.         size_t maxsize,
  252.         const char *format,
  253.         const struct tm *timeptr,
  254.                 void *lc_time_arg
  255.         )
  256. {
  257.         struct __lc_time_data *lc_time = lc_time_arg == 0 ? __lc_time_curr
  258.             : (struct __lc_time_data *)lc_time_arg;
  259.  
  260.         size_t left;                    /* space left in output string */
  261. #ifdef _MT
  262.         int local_lock_flag;
  263. #endif  /* _MT */
  264.  
  265.         /* Copy maxsize into temp. */
  266.         left = maxsize;
  267.  
  268.         _lock_locale( local_lock_flag )
  269.  
  270.         /* Copy the input string to the output string expanding the format
  271.         designations appropriately.  Stop copying when one of the following
  272.         is true: (1) we hit a null char in the input stream, or (2) there's
  273.         no room left in the output stream. */
  274.  
  275.         while (left > 0)
  276.         {
  277.                 switch(*format)
  278.                 {
  279.  
  280.                 case('\0'):
  281.  
  282.                         /* end of format input string */
  283.                         goto done;
  284.  
  285.                 case('%'):
  286.  
  287.                         /* Format directive.  Take appropriate action based
  288.                         on format control character. */
  289.  
  290.                         format++;                       /* skip over % char */
  291.  
  292.                         /* process flags */
  293.                         __alternate_form = 0;
  294.                         if (*format == '#')
  295.                         {
  296.                                 __alternate_form = 1;
  297.                                 format++;
  298.                         }
  299.                         _expandtime(*format, timeptr, &string, &left,
  300.                                                         lc_time);
  301.                         format++;                       /* skip format char */
  302.                         break;
  303.  
  304.  
  305.                 default:
  306.  
  307.                         /* store character, bump pointers, dec the char count */
  308.                         if (isleadbyte((int)(*format)) && left > 1)
  309.                         {
  310.                                 *string++ = *format++;
  311.                                 left--;
  312.                         }
  313.                         *string++ = *format++;
  314.                         left--;
  315.                         break;
  316.                 }
  317.         }
  318.  
  319.  
  320.         /* All done.  See if we terminated because we hit a null char or because
  321.         we ran out of space */
  322.  
  323.         done:
  324.  
  325.         _unlock_locale( local_lock_flag )
  326.  
  327.         if (left > 0) {
  328.  
  329.                 /* Store a terminating null char and return the number of chars
  330.                 we stored in the output string. */
  331.  
  332.                 *string = '\0';
  333.                 return(maxsize-left);
  334.         }
  335.  
  336.         else
  337.                 return(0);
  338.  
  339. }
  340.  
  341.  
  342. /***
  343. *_expandtime() - Expand the conversion specifier
  344. *
  345. *Purpose:
  346. *       Expand the given strftime conversion specifier using the time struct
  347. *       and store it in the supplied buffer.
  348. *
  349. *       The expansion is locale-dependent.
  350. *
  351. *       *** For internal use with strftime() only ***
  352. *
  353. *Entry:
  354. *       char specifier = strftime conversion specifier to expand
  355. *       const struct tm *tmptr = pointer to time/date structure
  356. *       char **string = address of pointer to output string
  357. *       size_t *count = address of char count (space in output area)
  358. *       struct __lc_time_data *lc_time = pointer to locale-specific info
  359. *
  360. *Exit:
  361. *       none
  362. *
  363. *Exceptions:
  364. *
  365. *******************************************************************************/
  366.  
  367. static void __cdecl _expandtime (
  368.         char specifier,
  369.         const struct tm *timeptr,
  370.         char **string,
  371.         size_t *left,
  372.         struct __lc_time_data *lc_time
  373.         )
  374. {
  375.         unsigned temp;                  /* temps */
  376.         int wdaytemp;
  377.  
  378.         /* Use a copy of the appropriate __lc_time_data pointer.  This
  379.         should prevent the necessity of locking/unlocking in mthread
  380.         code (if we can guarantee that the various __lc_time data
  381.         structures are always in the same segment). contents of time
  382.         strings structure can now change, so thus we do use locking */
  383.  
  384.         switch(specifier) {             /* switch on specifier */
  385.  
  386.                 case('a'):              /* abbreviated weekday name */
  387.                         _store_str((char *)(lc_time->wday_abbr[timeptr->tm_wday]),
  388.                                  string, left);
  389.                         break;
  390.  
  391.                 case('A'):              /* full weekday name */
  392.                         _store_str((char *)(lc_time->wday[timeptr->tm_wday]),
  393.                                  string, left);
  394.                         break;
  395.  
  396.                 case('b'):              /* abbreviated month name */
  397.                         _store_str((char *)(lc_time->month_abbr[timeptr->tm_mon]),
  398.                                  string, left);
  399.                         break;
  400.  
  401.                 case('B'):              /* full month name */
  402.                         _store_str((char *)(lc_time->month[timeptr->tm_mon]),
  403.                                  string, left);
  404.                         break;
  405.  
  406.                 case('c'):              /* date and time display */
  407.                         if (__alternate_form)
  408.                         {
  409.                                 __alternate_form = FALSE;
  410.                                 _store_winword(lc_time->ww_ldatefmt, timeptr, string, left, lc_time);
  411.                                 if (*left == 0)
  412.                                         return;
  413.                                 *(*string)++=' ';
  414.                                 (*left)--;
  415.                                 _store_winword(lc_time->ww_timefmt, timeptr, string, left, lc_time);
  416.                         }
  417.                         else {
  418.                                 _store_winword(lc_time->ww_sdatefmt, timeptr, string, left, lc_time);
  419.                                 if (*left == 0)
  420.                                         return;
  421.                                 *(*string)++=' ';
  422.                                 (*left)--;
  423.                                 _store_winword(lc_time->ww_timefmt, timeptr, string, left, lc_time);
  424.                         }
  425.                         break;
  426.  
  427.                 case('d'):              /* mday in decimal (01-31) */
  428.                         __no_lead_zeros = __alternate_form;
  429.                         _store_num(timeptr->tm_mday, 2, string, left);
  430.                         break;
  431.  
  432.                 case('H'):              /* 24-hour decimal (00-23) */
  433.                         __no_lead_zeros = __alternate_form;
  434.                         _store_num(timeptr->tm_hour, 2, string, left);
  435.                         break;
  436.  
  437.                 case('I'):              /* 12-hour decimal (01-12) */
  438.                         __no_lead_zeros = __alternate_form;
  439.                         if (!(temp = timeptr->tm_hour%12))
  440.                                 temp=12;
  441.                         _store_num(temp, 2, string, left);
  442.                         break;
  443.  
  444.                 case('j'):              /* yday in decimal (001-366) */
  445.                         __no_lead_zeros = __alternate_form;
  446.                         _store_num(timeptr->tm_yday+1, 3, string, left);
  447.                         break;
  448.  
  449.                 case('m'):              /* month in decimal (01-12) */
  450.                         __no_lead_zeros = __alternate_form;
  451.                         _store_num(timeptr->tm_mon+1, 2, string, left);
  452.                         break;
  453.  
  454.                 case('M'):              /* minute in decimal (00-59) */
  455.                         __no_lead_zeros = __alternate_form;
  456.                         _store_num(timeptr->tm_min, 2, string, left);
  457.                         break;
  458.  
  459.                 case('p'):              /* AM/PM designation */
  460.                         if (timeptr->tm_hour <= 11)
  461.                             _store_str((char *)(lc_time->ampm[0]), string, left);
  462.                         else
  463.                             _store_str((char *)(lc_time->ampm[1]), string, left);
  464.                         break;
  465.  
  466.                 case('S'):              /* secs in decimal (00-59) */
  467.                         __no_lead_zeros = __alternate_form;
  468.                         _store_num(timeptr->tm_sec, 2, string, left);
  469.                         break;
  470.  
  471.                 case('U'):              /* sunday week number (00-53) */
  472.                         __no_lead_zeros = __alternate_form;
  473.                         wdaytemp = timeptr->tm_wday;
  474.                         goto weeknum;   /* join common code */
  475.  
  476.                 case('w'):              /* week day in decimal (0-6) */
  477.                         __no_lead_zeros = __alternate_form;
  478.                         _store_num(timeptr->tm_wday, 1, string, left);
  479.                         break;
  480.  
  481.                 case('W'):              /* monday week number (00-53) */
  482.                         __no_lead_zeros = __alternate_form;
  483.                         if (timeptr->tm_wday == 0)  /* monday based */
  484.                                 wdaytemp = 6;
  485.                         else
  486.                                 wdaytemp = timeptr->tm_wday-1;
  487.                 weeknum:
  488.                         if (timeptr->tm_yday < wdaytemp)
  489.                                 temp=0;
  490.                         else {
  491.                                 temp = timeptr->tm_yday/7;
  492.                                 if ((timeptr->tm_yday%7) >= wdaytemp)
  493.                                         temp++;
  494.                                 }
  495.                         _store_num(temp, 2, string, left);
  496.                         break;
  497.  
  498.                 case('x'):              /* date display */
  499.                         if (__alternate_form)
  500.                         {
  501.                                 __alternate_form = FALSE;
  502.                                 _store_winword(lc_time->ww_ldatefmt, timeptr, string, left, lc_time);
  503.                         }
  504.                         else
  505.                         {
  506.                                 _store_winword(lc_time->ww_sdatefmt, timeptr, string, left, lc_time);
  507.                         }
  508.                         break;
  509.  
  510.                 case('X'):              /* time display */
  511.                         __alternate_form = FALSE;
  512.                         _store_winword(lc_time->ww_timefmt, timeptr, string, left, lc_time);
  513.                         break;
  514.  
  515.                 case('y'):              /* year w/o century (00-99) */
  516.                         __no_lead_zeros = __alternate_form;
  517.                         temp = timeptr->tm_year%100;
  518.                         _store_num(temp, 2, string, left);
  519.                         break;
  520.  
  521.                 case('Y'):              /* year w/ century */
  522.                         __no_lead_zeros = __alternate_form;
  523.                         temp = (((timeptr->tm_year/100)+19)*100) +
  524.                                (timeptr->tm_year%100);
  525.                         _store_num(temp, 4, string, left);
  526.                         break;
  527.  
  528.                 case('Z'):              /* time zone name, if any */
  529.                 case('z'):              /* time zone name, if any */
  530. #ifdef _MAC
  531.                         _tzset();       /* Set time zone info */
  532. #else  /* _MAC */
  533.                         __tzset();      /* Set time zone info */
  534. #endif  /* _MAC */
  535.                         _store_str(_tzname[((timeptr->tm_isdst)?1:0)],
  536.                                  string, left);
  537.                         break;
  538.  
  539.                 case('%'):              /* percent sign */
  540.                         *(*string)++ = '%';
  541.                         (*left)--;
  542.                         break;
  543.  
  544.                 default:                /* unknown format directive */
  545.                         /* ignore the directive and continue */
  546.                         /* [ANSI: Behavior is undefined.]    */
  547.                         break;
  548.  
  549.         }       /* end % switch */
  550. }
  551.  
  552.  
  553. /***
  554. *_store_str() - Copy a time string
  555. *
  556. *Purpose:
  557. *       Copy the supplied time string into the output string until
  558. *       (1) we hit a null in the time string, or (2) the given count
  559. *       goes to 0.
  560. *
  561. *       *** For internal use with strftime() only ***
  562. *
  563. *Entry:
  564. *       char *in = pointer to null terminated time string
  565. *       char **out = address of pointer to output string
  566. *       size_t *count = address of char count (space in output area)
  567. *
  568. *Exit:
  569. *       none
  570. *Exceptions:
  571. *
  572. *******************************************************************************/
  573.  
  574. static void __cdecl _store_str (
  575.         char *in,
  576.         char **out,
  577.         size_t *count
  578.         )
  579. {
  580.  
  581.         while ((*count != 0) && (*in != '\0')) {
  582.                 *(*out)++ = *in++;
  583.                 (*count)--;
  584.         }
  585. }
  586.  
  587.  
  588. /***
  589. *_store_num() - Convert a number to ascii and copy it
  590. *
  591. *Purpose:
  592. *       Convert the supplied number to decimal and store
  593. *       in the output buffer.  Update both the count and
  594. *       buffer pointers.
  595. *
  596. *       *** For internal use with strftime() only ***
  597. *
  598. *Entry:
  599. *       int num = pointer to integer value
  600. *       int digits = # of ascii digits to put into string
  601. *       char **out = address of pointer to output string
  602. *       size_t *count = address of char count (space in output area)
  603. *
  604. *Exit:
  605. *       none
  606. *Exceptions:
  607. *
  608. *******************************************************************************/
  609.  
  610. static void __cdecl _store_num (
  611.         int num,
  612.         int digits,
  613.         char **out,
  614.         size_t *count
  615.         )
  616. {
  617. int temp=0;
  618.  
  619.         if (__no_lead_zeros) {
  620.                 _store_number (num, out, count);
  621.                 return;
  622.         }
  623.  
  624.         if ((size_t)digits < *count)  {
  625.                 for (digits--; (digits+1); digits--) {
  626.                         (*out)[digits] = (char)('0' + num % 10);
  627.                         num /= 10;
  628.                         temp++;
  629.                 }
  630.                 *out += temp;
  631.                 *count -= temp;
  632.         }
  633. else
  634.         *count = 0;
  635. }
  636.  
  637. /***
  638. *_store_number() - Convert positive integer to string
  639. *
  640. *Purpose:
  641. *       Convert positive integer to a string and store it in the output
  642. *       buffer with no null terminator.  Update both the count and
  643. *       buffer pointers.
  644. *
  645. *       Differs from _store_num in that the precision is not specified,
  646. *       and no leading zeros are added.
  647. *
  648. *       *** For internal use with strftime() only ***
  649. *
  650. *       Created from xtoi.c
  651. *
  652. *Entry:
  653. *       int num = pointer to integer value
  654. *       char **out = address of pointer to output string
  655. *       size_t *count = address of char count (space in output area)
  656. *
  657. *Exit:
  658. *       none
  659. *
  660. *Exceptions:
  661. *       The buffer is filled until it is out of space.  There is no
  662. *       way to tell beforehand (as in _store_num) if the buffer will
  663. *       run out of space.
  664. *
  665. *******************************************************************************/
  666.  
  667. static void __cdecl _store_number (
  668.         int num,
  669.         char **out,
  670.         size_t *count
  671.         )
  672. {
  673.         char *p;                /* pointer to traverse string */
  674.         char *firstdig;         /* pointer to first digit */
  675.         char temp;              /* temp char */
  676.  
  677.         p = *out;
  678.  
  679.         /* put the digits in the buffer in reverse order */
  680.         if (*count > 1)
  681.         {
  682.                 do {
  683.                         *p++ = (char) (num % 10 + '0');
  684.                         (*count)--;
  685.                 } while ((num/=10) > 0 && *count > 1);
  686.         }
  687.  
  688.         firstdig = *out;                /* firstdig points to first digit */
  689.         *out = p;                       /* return pointer to next space */
  690.         p--;                            /* p points to last digit */
  691.  
  692.         /* reverse the buffer */
  693.         do {
  694.                 temp = *p;
  695.                 *p-- = *firstdig;
  696.                 *firstdig++ = temp;     /* swap *p and *firstdig */
  697.         } while (firstdig < p);         /* repeat until halfway */
  698. }
  699.  
  700.  
  701. /***
  702. *_store_winword() - Store date/time in WinWord format
  703. *
  704. *Purpose:
  705. *       Format the date/time in the supplied WinWord format
  706. *       and store it in the supplied buffer.
  707. *
  708. *       *** For internal use with strftime() only ***
  709. *
  710. *       The WinWord format is converted token by token to
  711. *       strftime conversion specifiers.  _expandtime is then called to
  712. *       do the work.  The WinWord format is expected to be a
  713. *       character string (not wide-chars).
  714. *
  715. *Entry:
  716. *       const char **format = address of pointer to WinWord format
  717. *       const struct tm *tmptr = pointer to time/date structure
  718. *       char **out = address of pointer to output string
  719. *       size_t *count = address of char count (space in output area)
  720. *       struct __lc_time_data *lc_time = pointer to locale-specific info
  721. *
  722. *Exit:
  723. *       none
  724. *
  725. *Exceptions:
  726. *
  727. *******************************************************************************/
  728.  
  729. static void __cdecl _store_winword (
  730.         const char *format,
  731.         const struct tm *tmptr,
  732.         char **out,
  733.         size_t *count,
  734.         struct __lc_time_data *lc_time
  735.         )
  736. {
  737.         char specifier;
  738.         const char *p;
  739.         int repeat;
  740.         char *ampmstr;
  741.  
  742.         while (*format && *count != 0)
  743.         {
  744.                 specifier = 0;          /* indicate no match */
  745.                 __no_lead_zeros = 0;    /* default is print leading zeros */
  746.  
  747.                 /* count the number of repetitions of this character */
  748.                 for (repeat=0, p=format; *p++ == *format; repeat++);
  749.                 /* leave p pointing to the beginning of the next token */
  750.                 p--;
  751.  
  752.                 /* switch on ascii format character and determine specifier */
  753.                 switch (*format)
  754.                 {
  755.                         case 'M':
  756.                                 switch (repeat)
  757.                                 {
  758.                                         case 1: __no_lead_zeros = 1; /* fall thru */
  759.                                         case 2: specifier = 'm'; break;
  760.                                         case 3: specifier = 'b'; break;
  761.                                         case 4: specifier = 'B'; break;
  762.                                 } break;
  763.                         case 'd':
  764.                                 switch (repeat)
  765.                                 {
  766.                                         case 1: __no_lead_zeros = 1; /* fall thru */
  767.                                         case 2: specifier = 'd'; break;
  768.                                         case 3: specifier = 'a'; break;
  769.                                         case 4: specifier = 'A'; break;
  770.                                 } break;
  771.                         case 'y':
  772.                                 switch (repeat)
  773.                                 {
  774.                                         case 2: specifier = 'y'; break;
  775.                                         case 4: specifier = 'Y'; break;
  776.                                 } break;
  777.                         case 'h':
  778.                                 switch (repeat)
  779.                                 {
  780.                                         case 1: __no_lead_zeros = 1; /* fall thru */
  781.                                         case 2: specifier = 'I'; break;
  782.                                 } break;
  783.                         case 'H':
  784.                                 switch (repeat)
  785.                                 {
  786.                                         case 1: __no_lead_zeros = 1; /* fall thru */
  787.                                         case 2: specifier = 'H'; break;
  788.                                 } break;
  789.                         case 'm':
  790.                                 switch (repeat)
  791.                                 {
  792.                                         case 1: __no_lead_zeros = 1; /* fall thru */
  793.                                         case 2: specifier = 'M'; break;
  794.                                 } break;
  795.                         case 's': /* for compatibility; not strictly WinWord */
  796.                                 switch (repeat)
  797.                                 {
  798.                                         case 1: __no_lead_zeros = 1; /* fall thru */
  799.                                         case 2: specifier = 'S'; break;
  800.                                 } break;
  801.                         case 'A':
  802.                         case 'a':
  803.                                 if (!_stricmp(format, "am/pm"))
  804.                                         p = format + 5;
  805.                                 else if (!_stricmp(format, "a/p"))
  806.                                         p = format + 3;
  807.                                 specifier = 'p';
  808.                                 break;
  809.                         case 't': /* t or tt time marker suffix */
  810.                                 if ( tmptr->tm_hour <= 11 )
  811.                                         ampmstr = lc_time->ampm[0];
  812.                                 else
  813.                                         ampmstr = lc_time->ampm[1];
  814.  
  815.                                 while ( (repeat > 0) && (*count > 0) )
  816.                                 {
  817.                                         if ( isleadbyte((int)*ampmstr) &&
  818.                                              (*count > 1) )
  819.                                         {
  820.                                                 *(*out)++ = *ampmstr++;
  821.                                                 (*count)--;
  822.                                         }
  823.                                         *(*out)++ = *ampmstr++;
  824.                                         (*count)--;
  825.                                         repeat--;
  826.                                 }
  827.                                 format = p;
  828.                                 continue;
  829.  
  830.                         case '\'': /* literal string */
  831.                                 if (repeat & 1) /* odd number */
  832.                                 {
  833.                                         format += repeat;
  834.                                         while (*format && *count != 0)
  835.                                         {
  836.                                                 if (*format == '\'')
  837.                                                 {
  838.                                                         format++;
  839.                                                         break;
  840.                                                 }
  841.                                                 if ( isleadbyte((int)*format) &&
  842.                                                      (*count > 1) )
  843.                                                 {
  844.                                                         *(*out)++ = *format++;
  845.                                                         (*count)--;
  846.                                                 }
  847.                                                 *(*out)++ = *format++;
  848.                                                 (*count)--;
  849.                                         }
  850.                                 }
  851.                                 else { /* even number */
  852.                                         format += repeat;
  853.                                 }
  854.                                 continue;
  855.  
  856.                         default: /* non-control char, print it */
  857.                                 break;
  858.                 } /* switch */
  859.  
  860.                 /* expand specifier, or copy literal if specifier not found */
  861.                 if (specifier)
  862.                 {
  863.                         _expandtime(specifier, tmptr, out, count, lc_time);
  864.                         format = p; /* bump format up to the next token */
  865.                 } else {
  866.                         if (isleadbyte((int)*format))
  867.                         {
  868.                                 *(*out)++ = *format++;
  869.                                 (*count)--;
  870.                         }
  871.                         *(*out)++ = *format++;
  872.                         (*count)--;
  873.                 }
  874.         } /* while */
  875. }
  876.  
  877.