Leaked source code of windows server 2003
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.

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