Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1152 lines
36 KiB

  1. /***
  2. *strftime.c - String Format Time
  3. *
  4. * Copyright (c) 1988-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *
  8. *Revision History:
  9. * 03-09-89 JCR Initial version.
  10. * 03-15-89 JCR Changed day/month strings from all caps to leading cap
  11. * 06-20-89 JCR Removed _LOAD_DGROUP code
  12. * 03-20-90 GJF Replaced _LOAD_DS with _CALLTYPE1, added #include
  13. * <cruntime.h>, removed #include <register.h> and
  14. * removed some leftover 16-bit support. Also, fixed
  15. * the copyright.
  16. * 03-23-90 GJF Made static functions _CALLTYPE4.
  17. * 07-23-90 SBM Compiles cleanly with -W3 (removed unreferenced
  18. * variable)
  19. * 08-13-90 SBM Compiles cleanly with -W3 with new build of compiler
  20. * 10-04-90 GJF New-style function declarators.
  21. * 01-22-91 GJF ANSI naming.
  22. * 08-15-91 MRM Calls tzset() to set timezone info in case of %z.
  23. * 08-16-91 MRM Put appropriate header file for tzset().
  24. * 10-10-91 ETC Locale support under _INTL switch.
  25. * 12-18-91 ETC Use localized time strings structure.
  26. * 02-10-93 CFW Ported to Cuda tree, change _CALLTYPE4 to _CRTAPI3.
  27. * 02-16-93 CFW Massive changes: bug fixes & enhancements.
  28. * 03-08-93 CFW Changed _expand to _expandtime.
  29. * 03-09-93 CFW Handle string literals inside format strings.
  30. * 03-09-93 CFW Alternate form cleanup.
  31. * 03-17-93 CFW Change *count > 0, to *count != 0, *count is unsigned.
  32. * 03-22-93 CFW Change "C" locale time format specifier to 24-hour.
  33. * 03-30-93 GJF Call _tzset instead of __tzset (which no longer
  34. * exists).
  35. * 04-06-93 SKS Replace _CRTAPI* with __cdecl
  36. * 04-14-93 CFW Disable _alternate_form for 'X' specifier, fix count bug.
  37. * 04-28-93 CFW Fix bug in '%c' handling.
  38. * 07-15-93 GJF Call __tzset() in place of _tzset().
  39. * 09-15-93 CFW Use ANSI conformant "__" names.
  40. * 04-11-94 GJF Made definitions of __lc_time_c, _alternate_form and
  41. * _no_lead_zeros conditional on ndef DLL_FOR_WIN32S.
  42. * 09-06-94 CFW Remove _INTL switch.
  43. * 02-13-95 GJF Appended Mac version of source file (somewhat cleaned
  44. * up), with appropriate #ifdef-s.
  45. * 09-26-95 GJF New locking macro, and scheme, for functions which
  46. * reference the locale.
  47. * 02-22-96 JWM Merge in PlumHall mods.
  48. * 06-17-96 SKS Enable new Plum-Hall code for _MAC as well as _WIN32
  49. * 07-10-97 GJF Made __lc_time_c selectany. Also, removed unnecessary
  50. * init to 0 for globals, cleaned up the formatting a bit,
  51. * added a few __cdecls and detailed old (and no longer
  52. * used as of 6/17/96 change) Mac version.
  53. * 08-21-97 GJF Added support for AM/PM type suffix to time string.
  54. * 09-10-98 GJF Added support for per-thread locale info.
  55. * 03-04-99 GJF Added refcount field to __lc_time_c.
  56. * 05-17-99 PML Remove all Macintosh support.
  57. * 08-30-99 PML Don't overflow buffer on leadbyte in _store_winword.
  58. * 03-17-00 PML Corrected _Gettnames to also copy ww_timefmt (VS7#9374)
  59. * 09-07-00 PML Remove dependency on libcp.lib/xlocinfo.h (vs7#159463)
  60. * 03-25-01 PML Use GetDateFormat/GetTimeFormat in _store_winword for
  61. * calendar types other than the basic type 1, localized
  62. * Gregorian (vs7#196892) Also fix formatting for leading
  63. * zero suppression in fields, which was busted for %c,
  64. * %x, %X.
  65. *
  66. *******************************************************************************/
  67. #include <cruntime.h>
  68. #include <internal.h>
  69. #include <mtdll.h>
  70. #include <time.h>
  71. #include <locale.h>
  72. #include <setlocal.h>
  73. #include <ctype.h>
  74. #include <stdlib.h>
  75. #include <string.h>
  76. #include <dbgint.h>
  77. #include <malloc.h>
  78. /* Prototypes for local routines */
  79. static void __cdecl _expandtime(
  80. #ifdef _MT
  81. pthreadlocinfo ptloci,
  82. #endif
  83. char specifier,
  84. const struct tm *tmptr,
  85. char **out,
  86. size_t *count,
  87. struct __lc_time_data *lc_time,
  88. unsigned alternate_form);
  89. static void __cdecl _store_str (char *in, char **out, size_t *count);
  90. static void __cdecl _store_num (int num, int digits, char **out, size_t *count,
  91. unsigned no_lead_zeros);
  92. static void __cdecl _store_number (int num, char **out, size_t *count);
  93. static void __cdecl _store_winword (
  94. #ifdef _MT
  95. pthreadlocinfo ptloci,
  96. #endif
  97. int field_code,
  98. const struct tm *tmptr,
  99. char **out,
  100. size_t *count,
  101. struct __lc_time_data *lc_time);
  102. size_t __cdecl _Strftime (
  103. char *string,
  104. size_t maxsize,
  105. const char *format,
  106. const struct tm *timeptr,
  107. void *lc_time_arg
  108. );
  109. #ifdef _MT
  110. size_t __cdecl _Strftime_mt (pthreadlocinfo ptloci, char *string, size_t maxsize,
  111. const char *format, const struct tm *timeptr, void *lc_time_arg);
  112. #endif
  113. /* LC_TIME data for local "C" */
  114. __declspec(selectany) struct __lc_time_data __lc_time_c = {
  115. {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"},
  116. {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
  117. "Friday", "Saturday", },
  118. {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
  119. "Sep", "Oct", "Nov", "Dec"},
  120. {"January", "February", "March", "April", "May", "June",
  121. "July", "August", "September", "October",
  122. "November", "December"},
  123. {"AM", "PM"},
  124. { "MM/dd/yy" },
  125. { "dddd, MMMM dd, yyyy" },
  126. { "HH:mm:ss" },
  127. 0x0409,
  128. 1,
  129. #ifdef _MT
  130. 0
  131. #endif
  132. };
  133. /* Pointer to the current LC_TIME data structure. */
  134. struct __lc_time_data *__lc_time_curr = &__lc_time_c;
  135. /* Codes for __lc_time_data ww_* fields for _store_winword */
  136. #define WW_SDATEFMT 0
  137. #define WW_LDATEFMT 1
  138. #define WW_TIMEFMT 2
  139. #define TIME_SEP ':'
  140. /* get a copy of the current day names */
  141. char * __cdecl _Getdays (
  142. void
  143. )
  144. {
  145. const struct __lc_time_data *pt = __lc_time_curr;
  146. size_t n, len = 0;
  147. char *p;
  148. for (n = 0; n < 7; ++n)
  149. len += strlen(pt->wday_abbr[n]) + strlen(pt->wday[n]) + 2;
  150. p = (char *)_malloc_crt(len + 1);
  151. if (p != 0) {
  152. char *s = p;
  153. for (n = 0; n < 7; ++n) {
  154. *s++ = TIME_SEP;
  155. s += strlen(strcpy(s, pt->wday_abbr[n]));
  156. *s++ = TIME_SEP;
  157. s += strlen(strcpy(s, pt->wday[n]));
  158. }
  159. *s++ = '\0';
  160. }
  161. return (p);
  162. }
  163. /* get a copy of the current month names */
  164. char * __cdecl _Getmonths (
  165. void
  166. )
  167. {
  168. const struct __lc_time_data *pt = __lc_time_curr;
  169. size_t n, len = 0;
  170. char *p;
  171. for (n = 0; n < 12; ++n)
  172. len += strlen(pt->month_abbr[n]) + strlen(pt->month[n]) + 2;
  173. p = (char *)_malloc_crt(len + 1);
  174. if (p != 0) {
  175. char *s = p;
  176. for (n = 0; n < 12; ++n) {
  177. *s++ = TIME_SEP;
  178. s += strlen(strcpy(s, pt->month_abbr[n]));
  179. *s++ = TIME_SEP;
  180. s += strlen(strcpy(s, pt->month[n]));
  181. }
  182. *s++ = '\0';
  183. }
  184. return (p);
  185. }
  186. /* get a copy of the current time locale information */
  187. void * __cdecl _Gettnames (
  188. void
  189. )
  190. {
  191. const struct __lc_time_data *pt = __lc_time_curr;
  192. size_t n, len = 0;
  193. void *p;
  194. for (n = 0; n < 7; ++n)
  195. len += strlen(pt->wday_abbr[n]) + strlen(pt->wday[n]) + 2;
  196. for (n = 0; n < 12; ++n)
  197. len += strlen(pt->month_abbr[n]) + strlen(pt->month[n]) + 2;
  198. len += strlen(pt->ampm[0]) + strlen(pt->ampm[1]) + 2;
  199. len += strlen(pt->ww_sdatefmt) + 1;
  200. len += strlen(pt->ww_ldatefmt) + 1;
  201. len += strlen(pt->ww_timefmt) + 1;
  202. p = _malloc_crt(sizeof (*pt) + len);
  203. if (p != 0) {
  204. struct __lc_time_data *pn = (struct __lc_time_data *)p;
  205. char *s = (char *)p + sizeof (*pt);
  206. memcpy(p, __lc_time_curr, sizeof (*pt));
  207. for (n = 0; n < 7; ++n) {
  208. pn->wday_abbr[n] = s;
  209. s += strlen(strcpy(s, pt->wday_abbr[n])) + 1;
  210. pn->wday[n] = s;
  211. s += strlen(strcpy(s, pt->wday[n])) + 1;
  212. }
  213. for (n = 0; n < 12; ++n) {
  214. pn->month_abbr[n] = s;
  215. s += strlen(strcpy(s, pt->month_abbr[n])) + 1;
  216. pn->month[n] = s;
  217. s += strlen(strcpy(s, pt->month[n])) + 1;
  218. }
  219. pn->ampm[0] = s;
  220. s += strlen(strcpy(s, pt->ampm[0])) + 1;
  221. pn->ampm[1] = s;
  222. s += strlen(strcpy(s, pt->ampm[1])) + 1;
  223. pn->ww_sdatefmt = s;
  224. s += strlen(strcpy(s, pt->ww_sdatefmt)) + 1;
  225. pn->ww_ldatefmt = s;
  226. s += strlen(strcpy(s, pt->ww_ldatefmt)) + 1;
  227. pn->ww_timefmt = s;
  228. strcpy(s, pt->ww_timefmt);
  229. }
  230. return (p);
  231. }
  232. /***
  233. *size_t strftime(string, maxsize, format, timeptr) - Format a time string
  234. *
  235. *Purpose:
  236. * Place characters into the user's output buffer expanding time
  237. * format directives as described in the user's control string.
  238. * Use the supplied 'tm' structure for time data when expanding
  239. * the format directives.
  240. * [ANSI]
  241. *
  242. *Entry:
  243. * char *string = pointer to output string
  244. * size_t maxsize = max length of string
  245. * const char *format = format control string
  246. * const struct tm *timeptr = pointer to tb data structure
  247. *
  248. *Exit:
  249. * !0 = If the total number of resulting characters including the
  250. * terminating null is not more than 'maxsize', then return the
  251. * number of chars placed in the 'string' array (not including the
  252. * null terminator).
  253. *
  254. * 0 = Otherwise, return 0 and the contents of the string are
  255. * indeterminate.
  256. *
  257. *Exceptions:
  258. *
  259. *******************************************************************************/
  260. size_t __cdecl strftime (
  261. char *string,
  262. size_t maxsize,
  263. const char *format,
  264. const struct tm *timeptr
  265. )
  266. {
  267. return (_Strftime(string, maxsize, format, timeptr, 0));
  268. }
  269. /***
  270. *size_t _Strftime(string, maxsize, format,
  271. * timeptr, lc_time) - Format a time string for a given locale
  272. *
  273. *Purpose:
  274. * Place characters into the user's output buffer expanding time
  275. * format directives as described in the user's control string.
  276. * Use the supplied 'tm' structure for time data when expanding
  277. * the format directives. use the locale information at lc_time.
  278. * [ANSI]
  279. *
  280. *Entry:
  281. * char *string = pointer to output string
  282. * size_t maxsize = max length of string
  283. * const char *format = format control string
  284. * const struct tm *timeptr = pointer to tb data structure
  285. * struct __lc_time_data *lc_time = pointer to locale-specific info
  286. * (passed as void * to avoid type mismatch with C++)
  287. *
  288. *Exit:
  289. * !0 = If the total number of resulting characters including the
  290. * terminating null is not more than 'maxsize', then return the
  291. * number of chars placed in the 'string' array (not including the
  292. * null terminator).
  293. *
  294. * 0 = Otherwise, return 0 and the contents of the string are
  295. * indeterminate.
  296. *
  297. *Exceptions:
  298. *
  299. *******************************************************************************/
  300. size_t __cdecl _Strftime (
  301. char *string,
  302. size_t maxsize,
  303. const char *format,
  304. const struct tm *timeptr,
  305. void *lc_time_arg
  306. )
  307. {
  308. #ifdef _MT
  309. pthreadlocinfo ptloci = _getptd()->ptlocinfo;
  310. if ( ptloci != __ptlocinfo )
  311. ptloci = __updatetlocinfo();
  312. return _Strftime_mt(ptloci, string, maxsize, format, timeptr,
  313. lc_time_arg);
  314. }
  315. size_t __cdecl _Strftime_mt (
  316. pthreadlocinfo ptloci,
  317. char *string,
  318. size_t maxsize,
  319. const char *format,
  320. const struct tm *timeptr,
  321. void *lc_time_arg
  322. )
  323. {
  324. #endif
  325. unsigned alternate_form;
  326. struct __lc_time_data *lc_time;
  327. size_t left; /* space left in output string */
  328. #ifdef _MT
  329. lc_time = lc_time_arg == 0 ? ptloci->lc_time_curr :
  330. #else
  331. lc_time = lc_time_arg == 0 ? __lc_time_curr :
  332. #endif
  333. (struct __lc_time_data *)lc_time_arg;
  334. /* Copy maxsize into temp. */
  335. left = maxsize;
  336. /* Copy the input string to the output string expanding the format
  337. designations appropriately. Stop copying when one of the following
  338. is true: (1) we hit a null char in the input stream, or (2) there's
  339. no room left in the output stream. */
  340. while (left > 0)
  341. {
  342. switch(*format)
  343. {
  344. case('\0'):
  345. /* end of format input string */
  346. goto done;
  347. case('%'):
  348. /* Format directive. Take appropriate action based
  349. on format control character. */
  350. format++; /* skip over % char */
  351. /* process flags */
  352. alternate_form = 0;
  353. if (*format == '#')
  354. {
  355. alternate_form = 1;
  356. format++;
  357. }
  358. #ifdef _MT
  359. _expandtime (ptloci, *format, timeptr, &string,
  360. #else
  361. _expandtime (*format, timeptr, &string,
  362. #endif
  363. &left,lc_time, alternate_form);
  364. format++; /* skip format char */
  365. break;
  366. default:
  367. /* store character, bump pointers, dec the char count */
  368. if (isleadbyte((int)(*format)) && left > 1)
  369. {
  370. *string++ = *format++;
  371. left--;
  372. }
  373. *string++ = *format++;
  374. left--;
  375. break;
  376. }
  377. }
  378. /* All done. See if we terminated because we hit a null char or because
  379. we ran out of space */
  380. done:
  381. if (left > 0) {
  382. /* Store a terminating null char and return the number of chars
  383. we stored in the output string. */
  384. *string = '\0';
  385. return(maxsize-left);
  386. }
  387. else
  388. return(0);
  389. }
  390. /***
  391. *_expandtime() - Expand the conversion specifier
  392. *
  393. *Purpose:
  394. * Expand the given strftime conversion specifier using the time struct
  395. * and store it in the supplied buffer.
  396. *
  397. * The expansion is locale-dependent.
  398. *
  399. * *** For internal use with strftime() only ***
  400. *
  401. *Entry:
  402. * char specifier = strftime conversion specifier to expand
  403. * const struct tm *tmptr = pointer to time/date structure
  404. * char **string = address of pointer to output string
  405. * size_t *count = address of char count (space in output area)
  406. * struct __lc_time_data *lc_time = pointer to locale-specific info
  407. *
  408. *Exit:
  409. * none
  410. *
  411. *Exceptions:
  412. *
  413. *******************************************************************************/
  414. static void __cdecl _expandtime (
  415. #ifdef _MT
  416. pthreadlocinfo ptloci,
  417. #endif
  418. char specifier,
  419. const struct tm *timeptr,
  420. char **string,
  421. size_t *left,
  422. struct __lc_time_data *lc_time,
  423. unsigned alternate_form
  424. )
  425. {
  426. unsigned temp; /* temps */
  427. int wdaytemp;
  428. /* Use a copy of the appropriate __lc_time_data pointer. This
  429. should prevent the necessity of locking/unlocking in mthread
  430. code (if we can guarantee that the various __lc_time data
  431. structures are always in the same segment). contents of time
  432. strings structure can now change, so thus we do use locking */
  433. switch(specifier) { /* switch on specifier */
  434. case('a'): /* abbreviated weekday name */
  435. _store_str((char *)(lc_time->wday_abbr[timeptr->tm_wday]),
  436. string, left);
  437. break;
  438. case('A'): /* full weekday name */
  439. _store_str((char *)(lc_time->wday[timeptr->tm_wday]),
  440. string, left);
  441. break;
  442. case('b'): /* abbreviated month name */
  443. _store_str((char *)(lc_time->month_abbr[timeptr->tm_mon]),
  444. string, left);
  445. break;
  446. case('B'): /* full month name */
  447. _store_str((char *)(lc_time->month[timeptr->tm_mon]),
  448. string, left);
  449. break;
  450. case('c'): /* date and time display */
  451. if (alternate_form)
  452. {
  453. _store_winword(
  454. #ifdef _MT
  455. ptloci,
  456. #endif
  457. WW_LDATEFMT, timeptr, string, left, lc_time);
  458. if (*left == 0)
  459. return;
  460. *(*string)++=' ';
  461. (*left)--;
  462. _store_winword(
  463. #ifdef _MT
  464. ptloci,
  465. #endif
  466. WW_TIMEFMT, timeptr, string, left, lc_time);
  467. }
  468. else {
  469. _store_winword(
  470. #ifdef _MT
  471. ptloci,
  472. #endif
  473. WW_SDATEFMT, timeptr, string, left, lc_time);
  474. if (*left == 0)
  475. return;
  476. *(*string)++=' ';
  477. (*left)--;
  478. _store_winword(
  479. #ifdef _MT
  480. ptloci,
  481. #endif
  482. WW_TIMEFMT, timeptr, string, left, lc_time);
  483. }
  484. break;
  485. case('d'): /* mday in decimal (01-31) */
  486. /* pass alternate_form as the no leading zeros flag */
  487. _store_num(timeptr->tm_mday, 2, string, left,
  488. alternate_form);
  489. break;
  490. case('H'): /* 24-hour decimal (00-23) */
  491. /* pass alternate_form as the no leading zeros flag */
  492. _store_num(timeptr->tm_hour, 2, string, left,
  493. alternate_form);
  494. break;
  495. case('I'): /* 12-hour decimal (01-12) */
  496. if (!(temp = timeptr->tm_hour%12))
  497. temp=12;
  498. /* pass alternate_form as the no leading zeros flag */
  499. _store_num(temp, 2, string, left, alternate_form);
  500. break;
  501. case('j'): /* yday in decimal (001-366) */
  502. /* pass alternate_form as the no leading zeros flag */
  503. _store_num(timeptr->tm_yday+1, 3, string, left,
  504. alternate_form);
  505. break;
  506. case('m'): /* month in decimal (01-12) */
  507. /* pass alternate_form as the no leading zeros flag */
  508. _store_num(timeptr->tm_mon+1, 2, string, left,
  509. alternate_form);
  510. break;
  511. case('M'): /* minute in decimal (00-59) */
  512. /* pass alternate_form as the no leading zeros flag */
  513. _store_num(timeptr->tm_min, 2, string, left,
  514. alternate_form);
  515. break;
  516. case('p'): /* AM/PM designation */
  517. if (timeptr->tm_hour <= 11)
  518. _store_str((char *)(lc_time->ampm[0]), string, left);
  519. else
  520. _store_str((char *)(lc_time->ampm[1]), string, left);
  521. break;
  522. case('S'): /* secs in decimal (00-59) */
  523. /* pass alternate_form as the no leading zeros flag */
  524. _store_num(timeptr->tm_sec, 2, string, left,
  525. alternate_form);
  526. break;
  527. case('U'): /* sunday week number (00-53) */
  528. wdaytemp = timeptr->tm_wday;
  529. goto weeknum; /* join common code */
  530. case('w'): /* week day in decimal (0-6) */
  531. /* pass alternate_form as the no leading zeros flag */
  532. _store_num(timeptr->tm_wday, 1, string, left,
  533. alternate_form);
  534. break;
  535. case('W'): /* monday week number (00-53) */
  536. if (timeptr->tm_wday == 0) /* monday based */
  537. wdaytemp = 6;
  538. else
  539. wdaytemp = timeptr->tm_wday-1;
  540. weeknum:
  541. if (timeptr->tm_yday < wdaytemp)
  542. temp = 0;
  543. else {
  544. temp = timeptr->tm_yday/7;
  545. if ((timeptr->tm_yday%7) >= wdaytemp)
  546. temp++;
  547. }
  548. /* pass alternate_form as the no leading zeros flag */
  549. _store_num(temp, 2, string, left, alternate_form);
  550. break;
  551. case('x'): /* date display */
  552. if (alternate_form)
  553. {
  554. _store_winword(
  555. #ifdef _MT
  556. ptloci,
  557. #endif
  558. WW_LDATEFMT, timeptr, string, left, lc_time);
  559. }
  560. else
  561. {
  562. _store_winword(
  563. #ifdef _MT
  564. ptloci,
  565. #endif
  566. WW_SDATEFMT, timeptr, string, left, lc_time);
  567. }
  568. break;
  569. case('X'): /* time display */
  570. _store_winword(
  571. #ifdef _MT
  572. ptloci,
  573. #endif
  574. WW_TIMEFMT, timeptr, string, left, lc_time);
  575. break;
  576. case('y'): /* year w/o century (00-99) */
  577. temp = timeptr->tm_year%100;
  578. /* pass alternate_form as the no leading zeros flag */
  579. _store_num(temp, 2, string, left, alternate_form);
  580. break;
  581. case('Y'): /* year w/ century */
  582. temp = (((timeptr->tm_year/100)+19)*100) +
  583. (timeptr->tm_year%100);
  584. /* pass alternate_form as the no leading zeros flag */
  585. _store_num(temp, 4, string, left, alternate_form);
  586. break;
  587. case('Z'): /* time zone name, if any */
  588. case('z'): /* time zone name, if any */
  589. #ifdef _POSIX_
  590. tzset(); /* Set time zone info */
  591. _store_str(tzname[((timeptr->tm_isdst)?1:0)],
  592. string, left);
  593. #else
  594. __tzset(); /* Set time zone info */
  595. _store_str(_tzname[((timeptr->tm_isdst)?1:0)],
  596. string, left);
  597. #endif
  598. break;
  599. case('%'): /* percent sign */
  600. *(*string)++ = '%';
  601. (*left)--;
  602. break;
  603. default: /* unknown format directive */
  604. /* ignore the directive and continue */
  605. /* [ANSI: Behavior is undefined.] */
  606. break;
  607. } /* end % switch */
  608. }
  609. /***
  610. *_store_str() - Copy a time string
  611. *
  612. *Purpose:
  613. * Copy the supplied time string into the output string until
  614. * (1) we hit a null in the time string, or (2) the given count
  615. * goes to 0.
  616. *
  617. * *** For internal use with strftime() only ***
  618. *
  619. *Entry:
  620. * char *in = pointer to null terminated time string
  621. * char **out = address of pointer to output string
  622. * size_t *count = address of char count (space in output area)
  623. *
  624. *Exit:
  625. * none
  626. *Exceptions:
  627. *
  628. *******************************************************************************/
  629. static void __cdecl _store_str (
  630. char *in,
  631. char **out,
  632. size_t *count
  633. )
  634. {
  635. while ((*count != 0) && (*in != '\0')) {
  636. *(*out)++ = *in++;
  637. (*count)--;
  638. }
  639. }
  640. /***
  641. *_store_num() - Convert a number to ascii and copy it
  642. *
  643. *Purpose:
  644. * Convert the supplied number to decimal and store
  645. * in the output buffer. Update both the count and
  646. * buffer pointers.
  647. *
  648. * *** For internal use with strftime() only ***
  649. *
  650. *Entry:
  651. * int num = pointer to integer value
  652. * int digits = # of ascii digits to put into string
  653. * char **out = address of pointer to output string
  654. * size_t *count = address of char count (space in output area)
  655. * unsigned no_lead_zeros = flag indicating that padding by leading
  656. * zeros is not necessary
  657. *
  658. *Exit:
  659. * none
  660. *Exceptions:
  661. *
  662. *******************************************************************************/
  663. static void __cdecl _store_num (
  664. int num,
  665. int digits,
  666. char **out,
  667. size_t *count,
  668. unsigned no_lead_zeros
  669. )
  670. {
  671. int temp = 0;
  672. if (no_lead_zeros) {
  673. _store_number (num, out, count);
  674. return;
  675. }
  676. if ((size_t)digits < *count) {
  677. for (digits--; (digits+1); digits--) {
  678. (*out)[digits] = (char)('0' + num % 10);
  679. num /= 10;
  680. temp++;
  681. }
  682. *out += temp;
  683. *count -= temp;
  684. }
  685. else
  686. *count = 0;
  687. }
  688. /***
  689. *_store_number() - Convert positive integer to string
  690. *
  691. *Purpose:
  692. * Convert positive integer to a string and store it in the output
  693. * buffer with no null terminator. Update both the count and
  694. * buffer pointers.
  695. *
  696. * Differs from _store_num in that the precision is not specified,
  697. * and no leading zeros are added.
  698. *
  699. * *** For internal use with strftime() only ***
  700. *
  701. * Created from xtoi.c
  702. *
  703. *Entry:
  704. * int num = pointer to integer value
  705. * char **out = address of pointer to output string
  706. * size_t *count = address of char count (space in output area)
  707. *
  708. *Exit:
  709. * none
  710. *
  711. *Exceptions:
  712. * The buffer is filled until it is out of space. There is no
  713. * way to tell beforehand (as in _store_num) if the buffer will
  714. * run out of space.
  715. *
  716. *******************************************************************************/
  717. static void __cdecl _store_number (
  718. int num,
  719. char **out,
  720. size_t *count
  721. )
  722. {
  723. char *p; /* pointer to traverse string */
  724. char *firstdig; /* pointer to first digit */
  725. char temp; /* temp char */
  726. p = *out;
  727. /* put the digits in the buffer in reverse order */
  728. if (*count > 1)
  729. {
  730. do {
  731. *p++ = (char) (num % 10 + '0');
  732. (*count)--;
  733. } while ((num/=10) > 0 && *count > 1);
  734. }
  735. firstdig = *out; /* firstdig points to first digit */
  736. *out = p; /* return pointer to next space */
  737. p--; /* p points to last digit */
  738. /* reverse the buffer */
  739. do {
  740. temp = *p;
  741. *p-- = *firstdig;
  742. *firstdig++ = temp; /* swap *p and *firstdig */
  743. } while (firstdig < p); /* repeat until halfway */
  744. }
  745. /***
  746. *_store_winword() - Store date/time in WinWord format
  747. *
  748. *Purpose:
  749. * Format the date/time in the supplied WinWord format
  750. * and store it in the supplied buffer.
  751. *
  752. * *** For internal use with strftime() only ***
  753. *
  754. * For simple localized Gregorian calendars (calendar type 1), the WinWord
  755. * format is converted token by token to strftime conversion specifiers.
  756. * _expandtime is then called to do the work. The WinWord format is
  757. * expected to be a character string (not wide-chars).
  758. *
  759. * For other calendar types, the Win32 APIs GetDateFormat/GetTimeFormat
  760. * are instead used to do all formatting, so that this routine doesn't
  761. * have to know about era/period strings, year offsets, etc.
  762. *
  763. *
  764. *Entry:
  765. * int field_code = code for ww_* field with format
  766. * const struct tm *tmptr = pointer to time/date structure
  767. * char **out = address of pointer to output string
  768. * size_t *count = address of char count (space in output area)
  769. * struct __lc_time_data *lc_time = pointer to locale-specific info
  770. *
  771. *Exit:
  772. * none
  773. *
  774. *Exceptions:
  775. *
  776. *******************************************************************************/
  777. static void __cdecl _store_winword (
  778. #ifdef _MT
  779. pthreadlocinfo ptloci,
  780. #endif
  781. int field_code,
  782. const struct tm *tmptr,
  783. char **out,
  784. size_t *count,
  785. struct __lc_time_data *lc_time
  786. )
  787. {
  788. const char *format;
  789. char specifier;
  790. const char *p;
  791. int repeat;
  792. char *ampmstr;
  793. unsigned no_lead_zeros;
  794. switch (field_code)
  795. {
  796. case WW_SDATEFMT:
  797. format = lc_time->ww_sdatefmt;
  798. break;
  799. case WW_LDATEFMT:
  800. format = lc_time->ww_ldatefmt;
  801. break;
  802. case WW_TIMEFMT:
  803. default:
  804. format = lc_time->ww_timefmt;
  805. break;
  806. }
  807. if (lc_time->ww_caltype != 1)
  808. {
  809. /* We have something other than the basic Gregorian calendar */
  810. SYSTEMTIME SystemTime;
  811. int cch;
  812. int (WINAPI * FormatFunc)(LCID, DWORD, const SYSTEMTIME *,
  813. LPCSTR, LPSTR, int);
  814. if (field_code != WW_TIMEFMT)
  815. FormatFunc = GetDateFormat;
  816. else
  817. FormatFunc = GetTimeFormat;
  818. SystemTime.wYear = (WORD)(tmptr->tm_year + 1900);
  819. SystemTime.wMonth = (WORD)(tmptr->tm_mon + 1);
  820. SystemTime.wDay = (WORD)(tmptr->tm_mday);
  821. SystemTime.wHour = (WORD)(tmptr->tm_hour);
  822. SystemTime.wMinute = (WORD)(tmptr->tm_min);
  823. SystemTime.wSecond = (WORD)(tmptr->tm_sec);
  824. SystemTime.wMilliseconds = 0;
  825. /* Find buffer size required */
  826. cch = FormatFunc(lc_time->ww_lcid, 0, &SystemTime,
  827. format, NULL, 0);
  828. if (cch != 0)
  829. {
  830. int malloc_flag = 0;
  831. char *buffer;
  832. /* Allocate buffer, first try stack, then heap */
  833. __try
  834. {
  835. buffer = (char *)_alloca(cch);
  836. }
  837. __except (EXCEPTION_EXECUTE_HANDLER)
  838. {
  839. _resetstkoflw();
  840. buffer = NULL;
  841. }
  842. if (buffer == NULL)
  843. {
  844. buffer = (char *)_malloc_crt(cch);
  845. if (buffer != NULL)
  846. malloc_flag = 1;
  847. }
  848. if (buffer != NULL)
  849. {
  850. /* Do actual date/time formatting */
  851. cch = FormatFunc(lc_time->ww_lcid, 0, &SystemTime,
  852. format, buffer, cch);
  853. /* Copy to output buffer */
  854. p = buffer;
  855. while (--cch > 0 && *count > 0) {
  856. *(*out)++ = *p++;
  857. (*count)--;
  858. }
  859. if (malloc_flag)
  860. _free_crt(buffer);
  861. return;
  862. }
  863. }
  864. /* In case of error, just fall through to localized Gregorian */
  865. }
  866. while (*format && *count != 0)
  867. {
  868. specifier = 0; /* indicate no match */
  869. no_lead_zeros = 0; /* default is print leading zeros */
  870. /* count the number of repetitions of this character */
  871. for (repeat=0, p=format; *p++ == *format; repeat++);
  872. /* leave p pointing to the beginning of the next token */
  873. p--;
  874. /* switch on ascii format character and determine specifier */
  875. switch (*format)
  876. {
  877. case 'M':
  878. switch (repeat)
  879. {
  880. case 1: no_lead_zeros = 1; /* fall thru */
  881. case 2: specifier = 'm'; break;
  882. case 3: specifier = 'b'; break;
  883. case 4: specifier = 'B'; break;
  884. } break;
  885. case 'd':
  886. switch (repeat)
  887. {
  888. case 1: no_lead_zeros = 1; /* fall thru */
  889. case 2: specifier = 'd'; break;
  890. case 3: specifier = 'a'; break;
  891. case 4: specifier = 'A'; break;
  892. } break;
  893. case 'y':
  894. switch (repeat)
  895. {
  896. case 2: specifier = 'y'; break;
  897. case 4: specifier = 'Y'; break;
  898. } break;
  899. case 'h':
  900. switch (repeat)
  901. {
  902. case 1: no_lead_zeros = 1; /* fall thru */
  903. case 2: specifier = 'I'; break;
  904. } break;
  905. case 'H':
  906. switch (repeat)
  907. {
  908. case 1: no_lead_zeros = 1; /* fall thru */
  909. case 2: specifier = 'H'; break;
  910. } break;
  911. case 'm':
  912. switch (repeat)
  913. {
  914. case 1: no_lead_zeros = 1; /* fall thru */
  915. case 2: specifier = 'M'; break;
  916. } break;
  917. case 's': /* for compatibility; not strictly WinWord */
  918. switch (repeat)
  919. {
  920. case 1: no_lead_zeros = 1; /* fall thru */
  921. case 2: specifier = 'S'; break;
  922. } break;
  923. case 'A':
  924. case 'a':
  925. if (!__ascii_stricmp(format, "am/pm"))
  926. p = format + 5;
  927. else if (!__ascii_stricmp(format, "a/p"))
  928. p = format + 3;
  929. specifier = 'p';
  930. break;
  931. case 't': /* t or tt time marker suffix */
  932. if ( tmptr->tm_hour <= 11 )
  933. ampmstr = lc_time->ampm[0];
  934. else
  935. ampmstr = lc_time->ampm[1];
  936. if ( (repeat == 1) && (*count > 0) ) {
  937. if (
  938. #ifdef _MT
  939. __isleadbyte_mt(ptloci, (int)*ampmstr) &&
  940. #else
  941. isleadbyte((int)*ampmstr) &&
  942. #endif
  943. (*count > 1) )
  944. {
  945. *(*out)++ = *ampmstr++;
  946. (*count)--;
  947. }
  948. *(*out)++ = *ampmstr++;
  949. (*count)--;
  950. } else {
  951. while (*ampmstr != 0 && *count > 0) {
  952. if (isleadbyte((int)*ampmstr) && *count > 1) {
  953. *(*out)++ = *ampmstr++;
  954. (*count)--;
  955. }
  956. *(*out)++ = *ampmstr++;
  957. (*count)--;
  958. }
  959. }
  960. format = p;
  961. continue;
  962. case '\'': /* literal string */
  963. if (repeat & 1) /* odd number */
  964. {
  965. format += repeat;
  966. while (*format && *count != 0)
  967. {
  968. if (*format == '\'')
  969. {
  970. format++;
  971. break;
  972. }
  973. #ifdef _MT
  974. if ( __isleadbyte_mt(ptloci, (int)*format) &&
  975. #else
  976. if ( isleadbyte((int)*format) &&
  977. #endif
  978. (*count > 1) )
  979. {
  980. *(*out)++ = *format++;
  981. (*count)--;
  982. }
  983. *(*out)++ = *format++;
  984. (*count)--;
  985. }
  986. }
  987. else { /* even number */
  988. format += repeat;
  989. }
  990. continue;
  991. default: /* non-control char, print it */
  992. break;
  993. } /* switch */
  994. /* expand specifier, or copy literal if specifier not found */
  995. if (specifier)
  996. {
  997. _expandtime(
  998. #ifdef _MT
  999. ptloci,
  1000. #endif
  1001. specifier, tmptr, out, count,
  1002. lc_time, no_lead_zeros);
  1003. format = p; /* bump format up to the next token */
  1004. } else {
  1005. #ifdef _MT
  1006. if (__isleadbyte_mt(ptloci, (int)*format) &&
  1007. #else
  1008. if (isleadbyte((int)*format) &&
  1009. #endif
  1010. (*count > 1))
  1011. {
  1012. *(*out)++ = *format++;
  1013. (*count)--;
  1014. }
  1015. *(*out)++ = *format++;
  1016. (*count)--;
  1017. }
  1018. } /* while */
  1019. }