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.

1275 lines
35 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. cclock.c
  5. Abstract:
  6. time/date functions
  7. --*/
  8. #include "cmd.h"
  9. #define MMDDYY 0
  10. #define DDMMYY 1
  11. #define YYMMDD 2
  12. extern TCHAR Fmt04[], Fmt05[], Fmt06[], Fmt10[], Fmt11[];
  13. extern TCHAR Fmt17[], Fmt15[];
  14. extern unsigned DosErr;
  15. extern unsigned LastRetCode;
  16. // for keeping current console output codepage.
  17. extern UINT CurrentCP;
  18. BOOL TimeAmPm=TRUE;
  19. TCHAR TimeSeparator[8];
  20. TCHAR DateSeparator[8];
  21. TCHAR DecimalPlace[8];
  22. int DateFormat;
  23. TCHAR *DateFormatString;
  24. TCHAR ThousandSeparator[8];
  25. TCHAR ShortMondayName[16];
  26. TCHAR ShortTuesdayName[16];
  27. TCHAR ShortWednesdayName[16];
  28. TCHAR ShortThursdayName[16];
  29. TCHAR ShortFridayName[16];
  30. TCHAR ShortSaturdayName[16];
  31. TCHAR ShortSundayName[16];
  32. TCHAR AMIndicator[8];
  33. TCHAR PMIndicator[8];
  34. ULONG YearWidth;
  35. //
  36. // We snapshot the current LCID at startup and modify it based on the current known
  37. // set of scripts that Console supports.
  38. //
  39. LCID CmdGetUserDefaultLCID(
  40. void
  41. )
  42. {
  43. LCID CmdLcid = GetUserDefaultLCID();
  44. #ifdef LANGPACK
  45. if (
  46. (PRIMARYLANGID(CmdLcid) == LANG_ARABIC) ||
  47. (PRIMARYLANGID(CmdLcid) == LANG_HEBREW) ||
  48. (PRIMARYLANGID(CmdLcid) == LANG_THAI) ||
  49. (PRIMARYLANGID(CmdLcid) == LANG_HINDI) ||
  50. (PRIMARYLANGID(CmdLcid) == LANG_TAMIL) ||
  51. (PRIMARYLANGID(CmdLcid) == LANG_FARSI)
  52. ) {
  53. CmdLcid = MAKELCID (MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT); // 0x409;
  54. }
  55. #endif
  56. return CmdLcid;
  57. }
  58. VOID
  59. InitLocale( VOID )
  60. {
  61. TCHAR Buffer[128];
  62. LCID CmdLcid = CmdGetUserDefaultLCID( );
  63. // get the time separator
  64. if (GetLocaleInfo(CmdLcid, LOCALE_STIME, Buffer, sizeof(TimeSeparator)))
  65. _tcscpy(TimeSeparator, Buffer);
  66. else
  67. _tcscpy(TimeSeparator, TEXT(":"));
  68. // determine if we're 0-12 or 0-24
  69. if (GetLocaleInfo(CmdLcid, LOCALE_ITIME, Buffer, 128)) {
  70. TimeAmPm = _tcscmp(Buffer,TEXT("1"));
  71. }
  72. _tcscpy(AMIndicator, TEXT("a"));
  73. _tcscpy(PMIndicator, TEXT("p"));
  74. //
  75. // get the date ordering
  76. //
  77. DateFormat = MMDDYY;
  78. if (GetLocaleInfo(CmdLcid, LOCALE_IDATE, Buffer, 128)) {
  79. switch (Buffer[0]) {
  80. case TEXT('0'):
  81. DateFormat = MMDDYY;
  82. DateFormatString = TEXT( "MM/dd/yy" );
  83. break;
  84. case TEXT('1'):
  85. DateFormat = DDMMYY;
  86. DateFormatString = TEXT( "dd/MM/yy" );
  87. break;
  88. case TEXT('2'):
  89. DateFormat = YYMMDD;
  90. DateFormatString = TEXT( "yy/MM/dd" );
  91. break;
  92. default:
  93. break;
  94. }
  95. }
  96. //
  97. // Get the date width
  98. //
  99. YearWidth = 2;
  100. if (GetLocaleInfo( CmdLcid, LOCALE_ICENTURY, Buffer, 128 )) {
  101. if (Buffer[0] == TEXT( '1' )) {
  102. YearWidth = 4;
  103. }
  104. }
  105. // get the date separator
  106. if (GetLocaleInfo(CmdLcid, LOCALE_SDATE, Buffer, sizeof(DateSeparator)))
  107. _tcscpy(DateSeparator, Buffer);
  108. else
  109. _tcscpy(DateSeparator, TEXT("/"));
  110. // get the short day names
  111. if (GetLocaleInfo(CmdLcid, LOCALE_SABBREVDAYNAME1, Buffer, sizeof(ShortMondayName)))
  112. _tcscpy(ShortMondayName, Buffer);
  113. else
  114. _tcscpy(ShortMondayName, TEXT("Mon"));
  115. if (GetLocaleInfo(CmdLcid, LOCALE_SABBREVDAYNAME2, Buffer, sizeof(ShortTuesdayName)))
  116. _tcscpy(ShortTuesdayName, Buffer);
  117. else
  118. _tcscpy(ShortTuesdayName, TEXT("Tue"));
  119. if (GetLocaleInfo(CmdLcid, LOCALE_SABBREVDAYNAME3, Buffer, sizeof(ShortWednesdayName)))
  120. _tcscpy(ShortWednesdayName, Buffer);
  121. else
  122. _tcscpy(ShortWednesdayName, TEXT("Wed"));
  123. if (GetLocaleInfo(CmdLcid, LOCALE_SABBREVDAYNAME4, Buffer, sizeof(ShortThursdayName)))
  124. _tcscpy(ShortThursdayName, Buffer);
  125. else
  126. _tcscpy(ShortThursdayName, TEXT("Thu"));
  127. if (GetLocaleInfo(CmdLcid, LOCALE_SABBREVDAYNAME5, Buffer, sizeof(ShortFridayName)))
  128. _tcscpy(ShortFridayName, Buffer);
  129. else
  130. _tcscpy(ShortFridayName, TEXT("Fri"));
  131. if (GetLocaleInfo(CmdLcid, LOCALE_SABBREVDAYNAME6, Buffer, sizeof(ShortSaturdayName)))
  132. _tcscpy(ShortSaturdayName, Buffer);
  133. else
  134. _tcscpy(ShortSaturdayName, TEXT("Sat"));
  135. if (GetLocaleInfo(CmdLcid, LOCALE_SABBREVDAYNAME7, Buffer, sizeof(ShortSundayName)))
  136. _tcscpy(ShortSundayName, Buffer);
  137. else
  138. _tcscpy(ShortSundayName, TEXT("Sun"));
  139. // get decimal and thousand separator strings
  140. if (GetLocaleInfo(CmdLcid, LOCALE_SDECIMAL, Buffer, sizeof(DecimalPlace)))
  141. _tcscpy(DecimalPlace, Buffer);
  142. else
  143. _tcscpy(DecimalPlace, TEXT("."));
  144. if (GetLocaleInfo(CmdLcid, LOCALE_STHOUSAND, Buffer, sizeof(ThousandSeparator)))
  145. _tcscpy(ThousandSeparator, Buffer);
  146. else
  147. _tcscpy(ThousandSeparator, TEXT(","));
  148. //
  149. // Set locale so that we can correctly process extended characters
  150. // Note: The string passed in is expected to be ASCII, not unicode
  151. //
  152. setlocale( LC_ALL, ".OCP" ) ;
  153. }
  154. BOOLEAN
  155. SetDateTime(
  156. IN LPSYSTEMTIME OsDateAndTime
  157. )
  158. {
  159. //
  160. // We have to do this twice in order to get the leap year set correctly.
  161. //
  162. SetLocalTime( OsDateAndTime );
  163. return(SetLocalTime( OsDateAndTime ) != 0);
  164. }
  165. /*** eDate - begin the execution of the Date command
  166. *
  167. * Purpose:
  168. * To display and/or set the system date.
  169. *
  170. * Args:
  171. * n - the parse tree node containing the date command
  172. *
  173. * int eDate(struct cmdnode *n)
  174. *
  175. * Returns:
  176. * SUCCESS always.
  177. *
  178. */
  179. int eDate(n)
  180. struct cmdnode *n ;
  181. {
  182. BOOL bTerse = FALSE;
  183. PTCHAR pArgs = n->argptr;
  184. DEBUG((CLGRP, DALVL, "eDATE: argptr = `%s'", n->argptr)) ;
  185. //
  186. // If extensions are enabled, allow a /T switch
  187. // to disable inputing a new DATE, just display the
  188. // current date.
  189. //
  190. if (fEnableExtensions)
  191. while ( (pArgs = mystrchr( pArgs, TEXT('/') )) != NULL ) {
  192. TCHAR c = (TCHAR) _totlower(*(pArgs+1));
  193. if ( c == TEXT('t') )
  194. bTerse = TRUE;
  195. pArgs += 2; // just skip it
  196. }
  197. if ( bTerse ) {
  198. PrintDate(NULL, PD_PTDATE, (TCHAR *)NULL, 0) ;
  199. cmd_printf(CrLf);
  200. return(LastRetCode = SUCCESS);
  201. }
  202. if ((n->argptr == NULL) ||
  203. (*(n->argptr = EatWS(n->argptr, NULL)) == NULLC)) {
  204. PutStdOut(MSG_CURRENT_DATE, NOARGS) ;
  205. PrintDate(NULL, PD_PTDATE, (TCHAR *)NULL, 0) ;
  206. cmd_printf(CrLf);
  207. };
  208. return(LastRetCode = GetVerSetDateTime(n->argptr, EDATE)) ;
  209. }
  210. /*** eTime - begin the execution of the Time command
  211. *
  212. * Purpose:
  213. * To display and/or set the system date.
  214. *
  215. * int eTime(struct cmdnode *n)
  216. *
  217. * Args:
  218. * n - the parse tree node containing the time command
  219. *
  220. * Returns:
  221. * SUCCESS always.
  222. *
  223. */
  224. int eTime(n)
  225. struct cmdnode *n ;
  226. {
  227. BOOL bTerse = FALSE;
  228. PTCHAR pArgs = n->argptr;
  229. DEBUG((CLGRP, TILVL, "eTIME: argptr = `%s'", n->argptr)) ;
  230. //
  231. // If extensions are enabled, allow a /T switch
  232. // to disable inputing a new TIME, just display the
  233. // current time.
  234. //
  235. if (fEnableExtensions)
  236. while ( (pArgs = mystrchr( pArgs, TEXT('/') )) != NULL ) {
  237. TCHAR c = (TCHAR) _totlower(*(pArgs+1));
  238. if ( c == TEXT('t') )
  239. bTerse = TRUE;
  240. pArgs += 2; // just skip it
  241. }
  242. if ( bTerse ) {
  243. PrintTime(NULL, PD_PTDATE, (TCHAR *)NULL, 0) ;
  244. cmd_printf(CrLf);
  245. return(LastRetCode = SUCCESS);
  246. }
  247. if ((n->argptr == NULL) ||
  248. (*(n->argptr = EatWS(n->argptr, NULL)) == NULLC)) {
  249. PutStdOut(MSG_CURRENT_TIME, NOARGS) ;
  250. PrintTime(NULL, PT_TIME, (TCHAR *)NULL, 0) ;
  251. cmd_printf(CrLf);
  252. };
  253. return(LastRetCode = GetVerSetDateTime(n->argptr, ETIME)) ;
  254. }
  255. /*** PrintDate - print the date
  256. *
  257. * Purpose:
  258. * To print the date either in the format used by the Date command or
  259. * the format used by the Dir command. The structure Cinfo is checked
  260. * for the country date format.
  261. *
  262. * PrintDate(int flag, TCHAR *buffer)
  263. *
  264. * Args:
  265. * flag - indicates which format to print
  266. * *buffer - indicates whether or not to print date message
  267. *
  268. * Notes:
  269. */
  270. int PrintDate(crt_time,flag,buffer,cch)
  271. struct tm *crt_time ;
  272. int flag ;
  273. TCHAR *buffer;
  274. int cch;
  275. {
  276. TCHAR DayOfWeek[10];
  277. TCHAR datebuf [32] ;
  278. unsigned i, j, k, m;
  279. int ptr = 0;
  280. struct tm xcrt_time ;
  281. SYSTEMTIME SystemTime;
  282. FILETIME FileTime;
  283. FILETIME LocalFileTime;
  284. int cchUsed;
  285. BOOL NeedDayOfWeek = TRUE;
  286. DEBUG((CLGRP, DALVL, "PRINTDATE: flag = %d", flag)) ;
  287. //
  288. // PrintDate is never called with PD_DATE and buffer == NULL
  289. // PrintDate is never called with PD_DIR and buffer == NULL
  290. // PrintDate is never called with PD_PTDATE and buffer != NULL
  291. //
  292. // Another way of saying this is:
  293. // PD_DATE => output to buffer
  294. // PD_DIR => output to buffer
  295. // PD_DIR2000 => output to buffer
  296. // PD_PTDATE => print out
  297. //
  298. // PD_DIR MM/DD/YY
  299. // PD_DIR2000 MM/DD/YYYY
  300. // PD_DATE Japan: MM/DD/YYYY DayOfWeek Rest: DayOfWeek MM/DD/YYYY
  301. // PD_PTDATE Japan: MM/DD/YYYY DayOfWeek Rest: DayOfWeek MM/DD/YYYY
  302. //
  303. //
  304. // If no time was input, then use the current system time. Convert from the
  305. // various formats to something standard.
  306. //
  307. if (!crt_time) {
  308. GetSystemTime(&SystemTime);
  309. SystemTimeToFileTime(&SystemTime,&FileTime);
  310. } else {
  311. xcrt_time = *crt_time;
  312. ConverttmToFILETIME(&xcrt_time,&FileTime);
  313. }
  314. FileTimeToLocalFileTime(&FileTime,&LocalFileTime);
  315. FileTimeToSystemTime( &LocalFileTime, &SystemTime );
  316. //
  317. // SystemTime now contains the correct local time
  318. // FileTime now contains the correct local time
  319. //
  320. //
  321. // If extensions are enabled, we format things in the culturally
  322. // correct format (from international control panel). if not, then
  323. // we display it as best we can from NT 4.
  324. //
  325. if (fEnableExtensions) {
  326. TCHAR LocaleDateFormat[128];
  327. PTCHAR p;
  328. BOOL InQuotes = FALSE;
  329. //
  330. // Map the locale to one that is acceptable to the console subsystem
  331. //
  332. if (!GetLocaleInfo( CmdGetUserDefaultLCID( ),
  333. LOCALE_SSHORTDATE,
  334. LocaleDateFormat,
  335. sizeof( LocaleDateFormat ) / sizeof( LocaleDateFormat[0] ))) {
  336. //
  337. // Not enough room for this format, cheat and use the one we
  338. // assumed from the DateFormat
  339. //
  340. _tcscpy( LocaleDateFormat, DateFormatString );
  341. }
  342. //
  343. // The format string may be expanded with widely varying widths. We
  344. // adjust this string to try to make sure that they are all fixed widths.
  345. //
  346. // The picture formats only have varying widths for:
  347. // d (no leading zero date)
  348. // dddd(full date name)
  349. // M (no leading zero month)
  350. // MMMM(full month name)
  351. //
  352. // So, if we see d or M, we change it to dd or MM (leading zero)
  353. // If we see dddd or MMMM we change it to ddd or MMM (three char abbrev)
  354. //
  355. p = LocaleDateFormat;
  356. while (*p != TEXT( '\0' )) {
  357. TCHAR c = *p;
  358. //
  359. // Text inside single quotes is left alone
  360. //
  361. if (c == TEXT( '\'' )) {
  362. InQuotes = !InQuotes;
  363. p++;
  364. } else if (InQuotes) {
  365. p++;
  366. } else if (c == TEXT( 'd' ) || c == TEXT( 'M' )) {
  367. //
  368. // Count the number of identical chars
  369. //
  370. int Count = 0;
  371. while (*p == c) {
  372. Count++;
  373. p++;
  374. }
  375. //
  376. // Reset p and shuffle string around based on the repetition count
  377. //
  378. p -= Count;
  379. if (Count == 1) {
  380. //
  381. // Move string right by one and copy the first char
  382. //
  383. memmove( (PUCHAR) &p[1], (PUCHAR) &p[0], sizeof( TCHAR ) * (_tcslen( &p[0] ) + 1));
  384. //
  385. // Skip over the format string
  386. //
  387. p += 2;
  388. } else {
  389. //
  390. // If the format string is specifying a day of week (d), then we do not
  391. // need to add on the DayOfWeek, below
  392. //
  393. if (c == TEXT( 'd' )) {
  394. NeedDayOfWeek = FALSE;
  395. }
  396. if (Count > 3) {
  397. //
  398. // Move string left from the first different char to just after the 3rd
  399. // repetition
  400. //
  401. memmove( (PUCHAR) &p[3], (PUCHAR) &p[Count], sizeof( TCHAR ) * (_tcslen( &p[Count] ) + 1));
  402. //
  403. // Skip over the format string
  404. //
  405. p += 3;
  406. } else {
  407. //
  408. // Skip over the 2 or 3 count
  409. //
  410. p += Count;
  411. }
  412. }
  413. } else {
  414. p++;
  415. }
  416. }
  417. GetDateFormat( CmdGetUserDefaultLCID( ),
  418. 0,
  419. &SystemTime,
  420. LocaleDateFormat,
  421. datebuf,
  422. sizeof( datebuf ) / sizeof( datebuf[0] ));
  423. } else {
  424. i = SystemTime.wMonth;
  425. j = SystemTime.wDay;
  426. k = SystemTime.wYear;
  427. //
  428. // only print last two digits for DIR listings
  429. //
  430. if (flag == PD_DIR) {
  431. k = k % 100;
  432. }
  433. if (DateFormat == YYMMDD ) {
  434. m = k ; /* Swap all values */
  435. k = j ;
  436. j = i ;
  437. i = m ;
  438. } else if (DateFormat == DDMMYY) {
  439. m = i ; /* Swap mon/day for Europe */
  440. i = j ;
  441. j = m ;
  442. }
  443. DEBUG((CLGRP, DALVL, "PRINTDATE: i = %d j = %d k = %d", i, j, k)) ;
  444. //
  445. // Format the current date and current day of week
  446. //
  447. _sntprintf(datebuf, 32, Fmt10, i, DateSeparator, j, DateSeparator, k);
  448. }
  449. _tcscpy( DayOfWeek, dayptr( SystemTime.wDayOfWeek )) ;
  450. //
  451. // If there is no input buffer, we display the day-of-week and date
  452. // according to language preference. Only in DBCS codepages (aka Japan)
  453. // does the day of week FOLLOW the date
  454. //
  455. if (buffer == NULL) {
  456. //
  457. // This can only be PD_PTDATE
  458. //
  459. //
  460. // No day of week means we simply display the date
  461. //
  462. if (!NeedDayOfWeek) {
  463. cchUsed = cmd_printf( Fmt11, datebuf );
  464. } else if (IsDBCSCodePage()) {
  465. cchUsed = cmd_printf( Fmt15, datebuf, DayOfWeek ); // "%s %s "
  466. } else {
  467. cchUsed = cmd_printf( Fmt15, DayOfWeek, datebuf ); // "%s %s "
  468. }
  469. } else {
  470. //
  471. // for PD_DATE, we need to output the date in the correct spot
  472. //
  473. if (NeedDayOfWeek && flag == PD_DATE) {
  474. if (IsDBCSCodePage()) {
  475. _tcscpy( buffer, datebuf );
  476. _tcscat( buffer, TEXT( " " ));
  477. _tcscat( buffer, DayOfWeek );
  478. } else {
  479. _tcscpy( buffer, DayOfWeek );
  480. _tcscat( buffer, TEXT( " " ));
  481. _tcscat( buffer, datebuf );
  482. }
  483. } else {
  484. //
  485. // PD_DIR and PD_DIR2000 only get the date
  486. //
  487. _tcscpy( buffer, datebuf );
  488. }
  489. cchUsed = _tcslen( buffer );
  490. }
  491. return cchUsed;
  492. }
  493. /*** PrintTime - print the time
  494. *
  495. * Purpose:
  496. * To print the time either in the format used by the Time command or
  497. * the format used by the Dir command. The structure Cinfo is checked
  498. * for the country time format.
  499. *
  500. * PrintTime(int flag)
  501. *
  502. * Args:
  503. * flag - indicates which format to print
  504. *
  505. */
  506. int PrintTime(crt_time, flag, buffer, cch)
  507. struct tm *crt_time ;
  508. int flag ;
  509. TCHAR *buffer;
  510. int cch;
  511. {
  512. TCHAR *ampm ;
  513. unsigned hr ;
  514. SYSTEMTIME SystemTime;
  515. FILETIME FileTime;
  516. FILETIME LocalFileTime;
  517. int cchUsed;
  518. if (!crt_time) {
  519. GetSystemTime(&SystemTime);
  520. SystemTimeToFileTime(&SystemTime,&FileTime);
  521. } else {
  522. ConverttmToFILETIME(crt_time,&FileTime);
  523. }
  524. FileTimeToLocalFileTime(&FileTime,&LocalFileTime);
  525. FileTimeToSystemTime( &LocalFileTime, &SystemTime );
  526. //
  527. // PT_TIME implies Time Command format. This is nothing more
  528. // than 24 hour clock with tenths
  529. //
  530. if (flag == PT_TIME) { /* Print time in Time command format */
  531. if (!buffer) {
  532. cchUsed = cmd_printf(Fmt06,
  533. SystemTime.wHour, TimeSeparator,
  534. SystemTime.wMinute, TimeSeparator,
  535. SystemTime.wSecond, DecimalPlace,
  536. SystemTime.wMilliseconds/10
  537. ) ;
  538. } else {
  539. cchUsed = _sntprintf(buffer, cch, Fmt06,
  540. SystemTime.wHour, TimeSeparator,
  541. SystemTime.wMinute, TimeSeparator,
  542. SystemTime.wSecond, DecimalPlace,
  543. SystemTime.wMilliseconds/10
  544. ) ;
  545. }
  546. } else {
  547. TCHAR TimeBuffer[32];
  548. //
  549. // Print time in Dir command format. If extensions are enabled
  550. // then we have the culturally correct time, otherwise we use
  551. // the NT 4 format.
  552. //
  553. if (fEnableExtensions) {
  554. TCHAR LocaleTimeFormat[128];
  555. PTCHAR p;
  556. BOOL InQuotes = FALSE;
  557. if (!GetLocaleInfo( CmdGetUserDefaultLCID( ),
  558. LOCALE_STIMEFORMAT,
  559. LocaleTimeFormat,
  560. sizeof( LocaleTimeFormat ) / sizeof( LocaleTimeFormat[0] ))) {
  561. //
  562. // Not enough room for this format, cheat and use the one we
  563. // assumed from the DateFormat
  564. //
  565. _tcscpy( LocaleTimeFormat, TEXT( "HH:mm:ss t" ));
  566. }
  567. //
  568. // Scan the string looking for "h", "H", or "m" and make sure there are two of them.
  569. // If there is a single one, replicate it. We do this to ensure leading zeros
  570. // which we need to make this a fixed-width string
  571. //
  572. p = LocaleTimeFormat;
  573. while (*p != TEXT( '\0' )) {
  574. TCHAR c = *p;
  575. //
  576. // Text inside single quotes is left alone
  577. //
  578. if (c == TEXT( '\'' )) {
  579. InQuotes = !InQuotes;
  580. p++;
  581. } else if (InQuotes) {
  582. p++;
  583. } else if (c == TEXT( 'h' ) || c == TEXT( 'H' ) || c == TEXT( 'm' )) {
  584. //
  585. // Count the number of identical chars
  586. //
  587. int Count = 0;
  588. while (*p == c) {
  589. Count++;
  590. p++;
  591. }
  592. //
  593. // Reset p and shuffle string around based on the repetition count
  594. //
  595. p -= Count;
  596. if (Count == 1) {
  597. memmove( (PUCHAR) &p[1], (PUCHAR) &p[0], sizeof( TCHAR ) * (_tcslen( &p[0] ) + 1));
  598. *p = c;
  599. }
  600. p++;
  601. }
  602. p++;
  603. }
  604. cchUsed = GetTimeFormat( CmdGetUserDefaultLCID( ),
  605. TIME_NOSECONDS,
  606. &SystemTime,
  607. LocaleTimeFormat,
  608. TimeBuffer,
  609. sizeof( TimeBuffer ) / sizeof( TimeBuffer[0] ));
  610. if (cchUsed == 0) {
  611. TimeBuffer[0] = TEXT( '\0' );
  612. }
  613. } else {
  614. ampm = AMIndicator ;
  615. hr = SystemTime.wHour;
  616. if ( TimeAmPm ) { /* 12 hour am/pm format */
  617. if ( hr >= 12) {
  618. if (hr > 12) {
  619. hr -= 12 ;
  620. }
  621. ampm = PMIndicator ;
  622. } else if (hr == 0) {
  623. hr = 12 ;
  624. }
  625. } else { /* 24 hour format */
  626. ampm = TEXT( " " );
  627. }
  628. _sntprintf( TimeBuffer,
  629. sizeof( TimeBuffer ) / sizeof( TimeBuffer[0] ),
  630. Fmt04,
  631. hr,
  632. TimeSeparator,
  633. SystemTime.wMinute,
  634. ampm );
  635. }
  636. if (!buffer) {
  637. cchUsed = CmdPutString( TimeBuffer );
  638. } else {
  639. _tcsncpy( buffer, TimeBuffer, cch );
  640. buffer[cch] = TEXT( '\0' );
  641. cchUsed = _tcslen( buffer );
  642. }
  643. }
  644. return cchUsed;
  645. }
  646. /*** GetVerSetDateTime - controls the changing of the date/time
  647. *
  648. * Purpose:
  649. * To prompt the user for a date or time, verify it, and set it.
  650. * On entry, if *dtstr is not '\0', it already points to a date or time
  651. * string.
  652. *
  653. * If null input is given to one of the prompts, the command execution
  654. * ends; neither the date or the time is changed.
  655. *
  656. * Once valid input has been received the date/time is updated.
  657. *
  658. * int GetVerSetDateTime(TCHAR *dtstr, int call)
  659. *
  660. * Args:
  661. * dtstr - ptr to command line date/time string and is used to hold a ptr
  662. * to the tokenized date/time string
  663. * call - indicates whether to prompt for date or time
  664. *
  665. */
  666. int GetVerSetDateTime(dtstr, call)
  667. TCHAR *dtstr ;
  668. int call ;
  669. {
  670. TCHAR dtseps[16] ; /* Date/Time separators passed to TokStr() */
  671. TCHAR *scan;
  672. TCHAR separators[16];
  673. TCHAR LocalBuf[MAX_PATH];
  674. unsigned int dformat ;
  675. SYSTEMTIME OsDateAndTime;
  676. LONG cbRead;
  677. int ret;
  678. if (call == EDATE) { /* Initialize date/time separator list */
  679. dtseps[0] = TEXT('/') ;
  680. dtseps[1] = TEXT('-') ;
  681. dtseps[2] = TEXT('.') ;
  682. _tcscpy(&dtseps[3], DateSeparator) ;
  683. } else {
  684. dtseps[0] = TEXT(':');
  685. dtseps[1] = TEXT('.');
  686. dtseps[2] = TimeSeparator[0] ;
  687. _tcscpy(&dtseps[3], DecimalPlace) ; /* decimal separator should */
  688. /* always be last */
  689. }
  690. DEBUG((CLGRP, DALVL|TILVL, "GVSDT: dtseps = `%s'", dtseps)) ;
  691. for ( ; ; ) { /* Date/time get-verify-set loop */
  692. if ((dtstr) && (*dtstr != NULLC)) { /* If a date/time was passed copy it into input buffer */
  693. _tcscpy(LocalBuf, dtstr) ;
  694. *dtstr = NULLC ;
  695. } else { /* Otherwise, prompt for new date/time */
  696. switch (DateFormat) { /* M012 */
  697. /* case USA: */
  698. case MMDDYY: /* @@ */
  699. dformat = MSG_ENTER_NEW_DATE ;
  700. break ;
  701. /* case JAPAN:
  702. case CHINA:
  703. case SWEDEN:
  704. case FCANADA: @@ */
  705. case YYMMDD:
  706. dformat = MSG_ENTER_JAPAN_DATE ;
  707. break ;
  708. default:
  709. dformat = MSG_ENTER_DEF_DATE ;
  710. } ;
  711. if ( call == EDATE )
  712. PutStdOut(dformat, ONEARG, DateSeparator );
  713. else
  714. PutStdOut(MSG_ENTER_NEW_TIME, NOARGS);
  715. scan = LocalBuf;
  716. ret = ReadBufFromInput(CRTTONT(STDIN),LocalBuf,MAX_PATH,&cbRead);
  717. if (ret && cbRead != 0) {
  718. *(scan + cbRead) = NULLC ;
  719. } else {
  720. //
  721. // attempt to read past eof or error in pipe
  722. // etc.
  723. //
  724. return( FAILURE );
  725. }
  726. for (scan = LocalBuf; *scan; scan++)
  727. if ( (*scan == '\n') || (*scan == '\r' )) {
  728. *scan = '\0';
  729. break;
  730. }
  731. if (!FileIsDevice(STDIN))
  732. cmd_printf(Fmt17, LocalBuf) ;
  733. DEBUG((CLGRP, DALVL|TILVL, "GVSDT: LocalBuf = `%s'", LocalBuf)) ;
  734. }
  735. _tcscpy( separators, dtseps);
  736. _tcscat( separators, TEXT(";") );
  737. if (*(dtstr = TokStr(LocalBuf,separators, TS_SDTOKENS )) == NULLC)
  738. return( SUCCESS ) ; /* If empty input, return */
  739. /* - Fill date/time buffer with correct date time and overlay that
  740. * of the user
  741. */
  742. GetLocalTime( &OsDateAndTime );
  743. if (((call == EDATE) ? VerifyDateString(&OsDateAndTime,dtstr,dtseps) :
  744. VerifyTimeString(&OsDateAndTime,dtstr,dtseps))) {
  745. if (SetDateTime( &OsDateAndTime )) {
  746. return( SUCCESS ) ;
  747. } else {
  748. if (GetLastError() == ERROR_PRIVILEGE_NOT_HELD) {
  749. PutStdErr(GetLastError(),NOARGS);
  750. return( FAILURE );
  751. }
  752. }
  753. }
  754. DEBUG((CLGRP, DALVL|TILVL, "GVSDT: Bad date/time entered.")) ;
  755. PutStdOut(((call == EDATE) ? MSG_INVALID_DATE : MSG_REN_INVALID_TIME), NOARGS);
  756. *dtstr = NULLC ;
  757. }
  758. return( SUCCESS );
  759. }
  760. /*** VerifyDateString - verifies a date string
  761. *
  762. * Purpose:
  763. * To verify a date string and load it into OsDateAndTime.
  764. *
  765. * VerifyDateString(TCHAR *dtoks, TCHAR *dseps)
  766. *
  767. * Args:
  768. * OsDateAndTime - where to store output numbers.
  769. * dtoks - tokenized date string
  770. * dseps - valid date separator characters
  771. *
  772. * Returns:
  773. * TRUE if the date string is valid.
  774. * FALSE if the date string is invalid.
  775. *
  776. */
  777. VerifyDateString(OsDateAndTime, dtoks, dseps)
  778. LPSYSTEMTIME OsDateAndTime ;
  779. TCHAR *dtoks ;
  780. TCHAR *dseps ;
  781. {
  782. int indexes[3] ; /* Storage for date elements */
  783. int i ; /* Work variable */
  784. int y, d, m ; /* Array indexes */
  785. switch (DateFormat) { /* Set array according to date format */
  786. case MMDDYY:
  787. m = 0 ;
  788. d = 1 ;
  789. y = 2 ;
  790. break ;
  791. case YYMMDD:
  792. y = 0 ;
  793. m = 1 ;
  794. d = 2 ;
  795. break ;
  796. default:
  797. d = 0 ;
  798. m = 1 ;
  799. y = 2 ;
  800. }
  801. DEBUG((CLGRP, DALVL, "VDATES: m = %d, d = %d, y = %d", m, d, y)) ;
  802. /* Loop through the tokens in dtoks, and load them into the array. Note
  803. * that the separators are also tokens in the string requiring the token
  804. * pointer to be advanced twice for each element.
  805. */
  806. for (i = 0 ; i < 3 ; i++, dtoks += _tcslen(dtoks)+1) {
  807. TCHAR *j;
  808. int Length;
  809. DEBUG((CLGRP, DALVL, "VDATES: i = %d dtoks = `%ws'", i, dtoks)) ;
  810. //
  811. // The atoi() return code will not suffice to reject date field strings with
  812. // non-digit characters. It is zero, both for error and for the valid integer
  813. // zero. Also, a string like "8$" will return 8. For that reason, each
  814. // character must be tested.
  815. //
  816. j = dtoks;
  817. while (*j != TEXT( '\0' )) {
  818. if (!_istdigit( *j )) {
  819. return FALSE;
  820. }
  821. j++;
  822. }
  823. //
  824. // Verify lengths:
  825. // years can be 2 or 4 chars in length
  826. // Days can be 1 or 2 chars in length
  827. // Months can be 1 or 2 chars in length
  828. //
  829. indexes[i] = _tcstol(dtoks, NULL, 10) ;
  830. Length = (int)(j - dtoks);
  831. if (i == y) {
  832. if (Length != 2 && Length != 4) {
  833. return FALSE;
  834. } else if (Length == 4 && indexes[i] < 1600) {
  835. return FALSE;
  836. }
  837. } else
  838. if (Length != 1 && Length != 2) {
  839. return FALSE;
  840. }
  841. dtoks = j + 1;
  842. DEBUG((CLGRP, DALVL, "VDATES: *dtoks = %02x", *dtoks)) ;
  843. if (i < 2 && (!*dtoks || !_tcschr(dseps, *dtoks)))
  844. return(FALSE) ;
  845. }
  846. //
  847. // FIX,FIX - need to calculate OsDateAndTime->wDayOfWeek
  848. //
  849. OsDateAndTime->wDay = (WORD)indexes[d] ;
  850. OsDateAndTime->wMonth = (WORD)indexes[m] ;
  851. //
  852. // Take two-digit years and convert them appropriately:
  853. // 80...99 => 1980...1999
  854. // 00...79 => 2000...2079
  855. //
  856. // Four-digit years are taken at face value
  857. //
  858. if (indexes[y] < 0) {
  859. return FALSE;
  860. } else if (00 <= indexes[y] && indexes[y] <= 79) {
  861. indexes[y] += 2000;
  862. } else if (80 <= indexes[y] && indexes[y] <= 99) {
  863. indexes[y] += 1900;
  864. } else if (100 <= indexes[y] && indexes[y] <= 1979) {
  865. return FALSE;
  866. }
  867. OsDateAndTime->wYear = (WORD)indexes[y] ;
  868. return(TRUE) ;
  869. }
  870. /*** VerifyTimeString - verifies a time string
  871. *
  872. * Purpose:
  873. * To verify a date string and load it into OsDateAndTime.
  874. *
  875. * VerifyTimeString(TCHAR *ttoks)
  876. *
  877. * Args: /
  878. * OsDateAndTime - where to store output numbers.
  879. * ttoks - Tokenized time string. NOTE: Each time field and each
  880. * separator field is an individual token in the time string.
  881. * Thus the token advancing formula "str += mystrlen(str)+1",
  882. * must be used twice to go from one time field to the next.
  883. *
  884. * Returns:
  885. * TRUE if the time string is valid.
  886. * FALSE if the time string is invalid.
  887. *
  888. */
  889. VerifyTimeString(OsDateAndTime, ttoks, tseps)
  890. LPSYSTEMTIME OsDateAndTime ;
  891. TCHAR *ttoks ;
  892. TCHAR *tseps ;
  893. {
  894. int i ; /* Work variables */
  895. int j ;
  896. TCHAR *p1, *p2;
  897. WORD *pp;
  898. TCHAR tsuffixes[] = TEXT("aApP");
  899. p2 = &tseps[ 1 ];
  900. pp = &OsDateAndTime->wHour;
  901. for (i = 0 ; i < 4 ; i++, ttoks += mystrlen(ttoks)+1) {
  902. DEBUG((CLGRP,TILVL, "VTIMES: ttoks = `%ws' i = %d", ttoks, i)) ;
  903. /* First insure that field is <= 2 bytes and they are digits. Note this
  904. * also verifies that field is present.
  905. */
  906. if ((j = mystrlen(ttoks)) > 2 ||
  907. !_istdigit(*ttoks) ||
  908. (*(ttoks+1) && !_istdigit(*(ttoks+1))))
  909. break;
  910. *pp++ = (TCHAR)_tcstol(ttoks, NULL, 10) ; /* Field OK, store int */
  911. ttoks += j+1 ; /* Adv to separator tok */
  912. DEBUG((CLGRP, TILVL, "VTIMES: separator = `%ws'", ttoks)) ;
  913. if (!*ttoks) /* No separator field? */
  914. break ; /* If so, exit loop */
  915. /* handle AM or PM
  916. */
  917. if (mystrchr(tsuffixes, *ttoks)) {
  918. goto HandleAMPM;
  919. }
  920. /* M000 - Fixed ability to use '.' as separator for time strings
  921. */
  922. if ( i < 2 ) {
  923. if ( ! (p1 = mystrchr(tseps, *ttoks) ) )
  924. return(FALSE) ;
  925. } else {
  926. if (*ttoks != *p2) /* Is decimal seperator */
  927. return(FALSE) ; /* valid. */
  928. }
  929. } ;
  930. //
  931. // see if there's an a or p specified. if there's a P, adjust
  932. // for PM time
  933. //
  934. if (*ttoks) {
  935. BOOL pm;
  936. if (!mystrchr(tsuffixes, *ttoks)) {
  937. return FALSE;
  938. }
  939. HandleAMPM:
  940. pm = (*ttoks == TEXT('p') || *ttoks == TEXT('P'));
  941. // if we're here, we've encountered an 'a' or 'p'. make
  942. // sure that it's the last character or that the only
  943. // character left is an 'm'. remember that since
  944. // 'a' and 'p' are separators, they get separated from the 'm'.
  945. ttoks += 2; // go past 'a' or 'p' plus null.
  946. if (*ttoks != NULLC &&
  947. *ttoks != TEXT('m') &&
  948. *ttoks != TEXT('M')) {
  949. return FALSE;
  950. }
  951. if (pm) {
  952. if (OsDateAndTime->wHour != 12) {
  953. OsDateAndTime->wHour += 12;
  954. }
  955. } else {
  956. if (OsDateAndTime->wHour == 12) {
  957. OsDateAndTime->wHour -= 12;
  958. }
  959. }
  960. }
  961. /* M002 - If we got at least one field, fill the rest with 00's
  962. */
  963. while (++i < 4)
  964. *pp++ = 0 ;
  965. return(TRUE) ;
  966. }
  967. VOID
  968. ConverttmToFILETIME (
  969. struct tm *Time,
  970. LPFILETIME FileTime
  971. )
  972. /*++
  973. Routine Description:
  974. This routine converts an NtTime value to its corresponding Fat time
  975. value.
  976. Arguments:
  977. Time - Supplies the C Runtime Time value to convert from
  978. FileTime - Receives the equivalent File date and time
  979. Return Value:
  980. BOOLEAN - TRUE if the Nt time value is within the range of Fat's
  981. time range, and FALSE otherwise
  982. --*/
  983. {
  984. SYSTEMTIME SystemTime;
  985. if (!Time) {
  986. GetSystemTime(&SystemTime);
  987. } else {
  988. //
  989. // Pack the input time/date into a system time record
  990. //
  991. SystemTime.wYear = (WORD)Time->tm_year;
  992. SystemTime.wMonth = (WORD)(Time->tm_mon+1); // C is [0..11]
  993. // NT is [1..12]
  994. SystemTime.wDay = (WORD)Time->tm_mday;
  995. SystemTime.wHour = (WORD)Time->tm_hour;
  996. SystemTime.wMinute = (WORD)Time->tm_min;
  997. SystemTime.wSecond = (WORD)Time->tm_sec;
  998. SystemTime.wDayOfWeek = (WORD)Time->tm_wday;
  999. SystemTime.wMilliseconds = 0;
  1000. }
  1001. SystemTimeToFileTime( &SystemTime, FileTime );
  1002. }
  1003. VOID
  1004. ConvertFILETIMETotm (
  1005. LPFILETIME FileTime,
  1006. struct tm *Time
  1007. )
  1008. /*++
  1009. Routine Description:
  1010. This routine converts a file time to its corresponding C Runtime time
  1011. value.
  1012. Arguments:
  1013. FileTime - Supplies the File date and time to convert from
  1014. Time - Receives the equivalent C Runtime Time value
  1015. Return Value:
  1016. --*/
  1017. {
  1018. SYSTEMTIME SystemTime;
  1019. // why skip printing the date if it's invalid?
  1020. //if (FileTime->dwLowDateTime == 0 && FileTime->dwHighDateTime == 0) {
  1021. // return( FALSE );
  1022. // }
  1023. FileTimeToSystemTime( FileTime, &SystemTime );
  1024. //
  1025. // Pack the input time/date into a time field record
  1026. //
  1027. Time->tm_year = SystemTime.wYear;
  1028. Time->tm_mon = SystemTime.wMonth-1; // NT is [1..12]
  1029. // C is [0..11]
  1030. Time->tm_mday = SystemTime.wDay;
  1031. Time->tm_hour = SystemTime.wHour;
  1032. Time->tm_min = SystemTime.wMinute;
  1033. Time->tm_sec = SystemTime.wSecond;
  1034. Time->tm_wday = SystemTime.wDayOfWeek;
  1035. Time->tm_yday = 0;
  1036. Time->tm_isdst = 0;
  1037. }