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.

1021 lines
28 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. console.c
  5. Abstract:
  6. This module implements the interfaces that
  7. provide access to the console i/o.
  8. Author:
  9. Wesley Witt (wesw) 21-Oct-1998
  10. Revision History:
  11. --*/
  12. #include "cmdcons.h"
  13. #pragma hdrstop
  14. //
  15. // Notes:
  16. //
  17. // This code needs to be correct for DBCS. A double-byte character takes up
  18. // 2 character spaces on the screen. This means that there is not a one-to-one
  19. // correlation between the unicode characters we want to work with and the
  20. // representation on the screen. For this reason, the code in this module
  21. // does a lot of converting into the OEM charset. When represented in the
  22. // OEM charset, strlen(x) is exactly the number of char spaces taken up
  23. // on the screen. (The fonts used in text mode setup are all OEM charset fonts
  24. // so that's why we use OEM).
  25. //
  26. #define CURSOR SplangGetCursorChar()
  27. #define CONSOLE_HEADER_HEIGHT 0
  28. unsigned ConsoleX,ConsoleY;
  29. UCHAR ConsoleLeadByteTable[128/8];
  30. BOOLEAN ConsoleDbcs;
  31. #define IS_LEAD_BYTE(c) ((c < 0x80) ? FALSE : (ConsoleLeadByteTable[(c & 0x7f) / 8] & (1 << ((c & 0x7f) % 8))))
  32. #define SET_LEAD_BYTE(c) (ConsoleLeadByteTable[(c & 0x7f)/8] |= (1 << ((c & 0x7f) % 8)))
  33. PUCHAR ConsoleOemString;
  34. ULONG ConsoleMaxOemStringLen;
  35. #define CONSOLE_ATTRIBUTE (ATT_FG_WHITE | ATT_BG_BLACK)
  36. #define CONSOLE_BACKGROUND ATT_BLACK
  37. //
  38. // Globals used for more mode.
  39. //
  40. BOOLEAN pMoreMode;
  41. unsigned pMoreLinesOut;
  42. unsigned pMoreMaxLines;
  43. #define CONSOLE_MORE_ATTRIBUTE (ATT_FG_BLACK | ATT_BG_WHITE)
  44. #define CONSOLE_MORE_BACKGROUND ATT_WHITE
  45. BOOLEAN
  46. pRcLineDown(
  47. BOOLEAN *pbScrolled
  48. );
  49. VOID
  50. RcConsoleInit(
  51. VOID
  52. )
  53. {
  54. unsigned i;
  55. //
  56. // Build lead-byte table, set ConsoleDbcs global
  57. //
  58. RtlZeroMemory(ConsoleLeadByteTable,sizeof(ConsoleLeadByteTable));
  59. if(ConsoleDbcs = NLS_MB_OEM_CODE_PAGE_TAG) {
  60. for(i=128; i<=255; i++) {
  61. if((NLS_OEM_LEAD_BYTE_INFO)[i]) {
  62. SET_LEAD_BYTE(i);
  63. }
  64. }
  65. }
  66. //
  67. // Get a buffer for unicode to oem translations.
  68. //
  69. ConsoleMaxOemStringLen = 2000;
  70. ConsoleOemString = SpMemAlloc(ConsoleMaxOemStringLen);
  71. //
  72. // Clear screen and initialize cursor location.
  73. //
  74. pRcCls();
  75. }
  76. VOID
  77. RcConsoleTerminate(
  78. VOID
  79. )
  80. {
  81. ASSERT(ConsoleOemString);
  82. SpMemFree(ConsoleOemString);
  83. ConsoleOemString = NULL;
  84. ConsoleMaxOemStringLen = 0;
  85. }
  86. #define MAX_HISTORY_LINES 30
  87. typedef struct _LINE_HISTORY {
  88. WCHAR Line[RC_MAX_LINE_LEN];
  89. ULONG Length;
  90. } LINE_HISTORY, *PLINE_HISTORY;
  91. LINE_HISTORY LineHistory[MAX_HISTORY_LINES];
  92. ULONG CurPos;
  93. ULONG NextPos;
  94. void
  95. RcPurgeHistoryBuffer(
  96. void
  97. )
  98. {
  99. CurPos = 0;
  100. NextPos = 0;
  101. ZeroMemory( LineHistory, sizeof(LineHistory) );
  102. }
  103. void
  104. RcClearToEOL(
  105. void
  106. )
  107. {
  108. unsigned uWidth = _CmdConsBlock->VideoVars->ScreenWidth;
  109. unsigned uY = ConsoleY + (ConsoleX / uWidth);
  110. unsigned uX = ConsoleX % uWidth; // taking care of roll over
  111. SpvidClearScreenRegion(uX, uY, uWidth-uX,
  112. 1, CONSOLE_BACKGROUND);
  113. }
  114. void
  115. RcClearLines(
  116. unsigned uX, unsigned uY, unsigned cLines
  117. )
  118. /*++
  119. Routine Description:
  120. This routine clears the specified number of lines with blank characters
  121. starting from X coordinate (0 based) on lines specifed by Y coordinate
  122. Arguments:
  123. uX - starting X coordinate
  124. uY - starting Y coordinate
  125. cLines - number of lines to be cleared after Y coordinate
  126. Return Value: None
  127. --*/
  128. {
  129. unsigned uWidth = _CmdConsBlock->VideoVars->ScreenWidth;
  130. if (uY < _CmdConsBlock->VideoVars->ScreenWidth) {
  131. SpvidClearScreenRegion(uX, uY, uWidth-uX,
  132. 1, CONSOLE_BACKGROUND);
  133. if (cLines && (cLines <= _CmdConsBlock->VideoVars->ScreenWidth)) {
  134. SpvidClearScreenRegion(0, ++uY, uWidth,
  135. cLines, CONSOLE_BACKGROUND);
  136. }
  137. }
  138. }
  139. unsigned
  140. _RcLineIn(
  141. OUT PWCHAR Buffer,
  142. IN unsigned MaxLineLen,
  143. IN BOOLEAN PasswordProtect,
  144. IN BOOLEAN UseBuffer
  145. )
  146. /*++
  147. Routine Description:
  148. Get a line of input from the user. The user can type at the keyboard.
  149. Control is very simple, the only control character accepted is backspace.
  150. A cursor will be drawn as the user types to indicate where the next
  151. character will end up on the screen. As the user types the screen will be
  152. scrolled if necessary.
  153. The string returned will be limited to MaxLineLen-1 characters and
  154. 0-terminated.
  155. NOTE: this routine deals with double-byte characters correctly.
  156. Arguments:
  157. Buffer - receives the line as typed by the user. The buffer must be
  158. large enough to hold least 2 characters, since the string returned
  159. will always get a nul-termination and requesting a string that
  160. can have at most only a terminating nul isn't too meaningful.
  161. MaxLineLen - supplies the number of unicode characters that will fit
  162. in the buffer pointed to by Buffer (including the terminating nul).
  163. As described above, must be > 1.
  164. Return Value:
  165. Number of characters written into Buffer, not including the terminating
  166. nul character.
  167. Upon return the global ConsoleX and ConsoleY variables will be updated
  168. such that ConsoleX is 0 and ConsoleY indicates the next "empty" line.
  169. Also the cursor will be shut off.
  170. --*/
  171. {
  172. unsigned LineLen;
  173. ULONG c;
  174. WCHAR s[2];
  175. UCHAR Oem[3];
  176. BOOL Done;
  177. ULONG OemLen;
  178. int i,j;
  179. ULONG OrigConsoleX;
  180. ULONG ulOrigY;
  181. BOOLEAN bScrolled = FALSE;
  182. ASSERT(MaxLineLen > 1);
  183. MaxLineLen--; // leave room for terminating nul
  184. LineLen = 0;
  185. Done = FALSE;
  186. s[1] = 0;
  187. //
  188. // We use ConsoleOemString as temp storage for char lengths.
  189. // Make sure we don't run off the end of the buffer.
  190. //
  191. if(MaxLineLen > ConsoleMaxOemStringLen) {
  192. MaxLineLen = ConsoleMaxOemStringLen;
  193. }
  194. //
  195. // Turn cursor on.
  196. //
  197. s[0] = CURSOR;
  198. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  199. //
  200. // Get characters until user hits enter.
  201. //
  202. CurPos = NextPos;
  203. OrigConsoleX = ConsoleX;
  204. ulOrigY = ConsoleY;
  205. if (UseBuffer) {
  206. LineLen = wcslen(Buffer);
  207. RtlZeroMemory(ConsoleOemString,ConsoleMaxOemStringLen);
  208. RtlUnicodeToOemN(ConsoleOemString,ConsoleMaxOemStringLen,&OemLen,Buffer,LineLen*sizeof(WCHAR));
  209. SpvidDisplayOemString(ConsoleOemString,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  210. ConsoleX += OemLen;
  211. RcClearToEOL();
  212. s[0] = CURSOR;
  213. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  214. for (i=0; i<(int)wcslen(Buffer); i++) {
  215. RtlUnicodeToOemN(Oem,3,&OemLen,&Buffer[i],2*sizeof(WCHAR));
  216. ConsoleOemString[i] = (UCHAR)OemLen-1;
  217. }
  218. }
  219. do {
  220. c = SpInputGetKeypress();
  221. if(c & KEY_NON_CHARACTER) {
  222. if (c == KEY_UP || c == KEY_DOWN) {
  223. if (c == KEY_UP) {
  224. if (CurPos == 0) {
  225. i = MAX_HISTORY_LINES - 1;
  226. } else {
  227. i = CurPos - 1;
  228. }
  229. j = i;
  230. while (LineHistory[i].Length == 0) {
  231. i -= 1;
  232. if (i < 0) {
  233. i = MAX_HISTORY_LINES - 1;
  234. }
  235. if (i == j) break;
  236. }
  237. }
  238. if (c == KEY_DOWN) {
  239. if (CurPos == MAX_HISTORY_LINES) {
  240. i = 0;
  241. } else {
  242. i = CurPos + 1;
  243. }
  244. j = i;
  245. while (LineHistory[i].Length == 0) {
  246. i += 1;
  247. if (i == MAX_HISTORY_LINES) {
  248. i = 0;
  249. }
  250. if (i == j) break;
  251. }
  252. }
  253. if (LineHistory[i].Length) {
  254. LineLen = LineHistory[i].Length;
  255. if(LineLen > MaxLineLen) {
  256. LineLen = MaxLineLen;
  257. }
  258. RtlCopyMemory(Buffer, LineHistory[i].Line, LineLen * sizeof(WCHAR));
  259. Buffer[LineLen] = 0;
  260. RtlZeroMemory(ConsoleOemString,ConsoleMaxOemStringLen);
  261. RtlUnicodeToOemN(ConsoleOemString,ConsoleMaxOemStringLen,&OemLen,Buffer,LineLen*sizeof(WCHAR));
  262. ConsoleX = OrigConsoleX;
  263. // clear the old command
  264. RcClearLines(ConsoleX, ulOrigY, ConsoleY - ulOrigY);
  265. ConsoleY = ulOrigY;
  266. // scroll if needed
  267. if ((ConsoleX + OemLen) >= _CmdConsBlock->VideoVars->ScreenWidth) {
  268. int cNumLines = (ConsoleX + OemLen) /
  269. _CmdConsBlock->VideoVars->ScreenWidth;
  270. int cAvailLines = _CmdConsBlock->VideoVars->ScreenHeight -
  271. ConsoleY - 1;
  272. if (cNumLines > cAvailLines) {
  273. cNumLines -= cAvailLines;
  274. SpvidScrollUp( CONSOLE_HEADER_HEIGHT,
  275. _CmdConsBlock->VideoVars->ScreenHeight - 1,
  276. cNumLines,
  277. CONSOLE_BACKGROUND
  278. );
  279. ConsoleY -= cNumLines;
  280. ulOrigY = ConsoleY;
  281. }
  282. }
  283. SpvidDisplayOemString(ConsoleOemString,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  284. // clean up trailing spaces left by previous command
  285. ConsoleX += OemLen;
  286. //RcClearToEOL();
  287. s[0] = CURSOR;
  288. if (ConsoleX >= _CmdConsBlock->VideoVars->ScreenWidth) {
  289. ConsoleY += (ConsoleX / _CmdConsBlock->VideoVars->ScreenWidth);
  290. ConsoleY %= _CmdConsBlock->VideoVars->ScreenHeight;
  291. ConsoleX %= _CmdConsBlock->VideoVars->ScreenWidth;
  292. }
  293. SpvidDisplayString(s,CONSOLE_ATTRIBUTE, ConsoleX, ConsoleY);
  294. CurPos = i;
  295. for (i=0; i<(int)wcslen(Buffer); i++) {
  296. RtlUnicodeToOemN(Oem,3,&OemLen,&Buffer[i],2*sizeof(WCHAR));
  297. ConsoleOemString[i] = (UCHAR)OemLen-1;
  298. }
  299. }
  300. }
  301. } else {
  302. //
  303. // Got a real unicode value, which could be CR, etc.
  304. //
  305. s[0] = (WCHAR)c;
  306. switch(s[0]) {
  307. case ASCI_ESC:
  308. LineLen = 0;
  309. ConsoleX = OrigConsoleX;
  310. CurPos = NextPos;
  311. // clear the extra lines from previous command if any
  312. RcClearLines(ConsoleX, ulOrigY, ConsoleY - ulOrigY);
  313. //RcClearToEOL();
  314. s[0] = CURSOR;
  315. ConsoleY = ulOrigY;
  316. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  317. break;
  318. case ASCI_BS:
  319. if(LineLen) {
  320. LineLen--;
  321. //
  322. // Write a space over the current cursor location
  323. // and then back up one char and write the cursor.
  324. //
  325. s[0] = L' ';
  326. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  327. OemLen = ConsoleOemString[LineLen];
  328. ASSERT(OemLen <= 2);
  329. if(ConsoleX) {
  330. //
  331. // We might have the case where the character we just erased
  332. // is a double-byte character that didn't fit on the previous
  333. // line because the user typed it when the cursor was at the
  334. // rightmost x position.
  335. //
  336. if(OemLen) {
  337. //
  338. // No special case needed. Decrement the x position and
  339. // clear out the second half of double-byte char,
  340. // if necessary.
  341. //
  342. ConsoleX -= OemLen;
  343. if(OemLen == 2) {
  344. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX+1,ConsoleY);
  345. }
  346. } else {
  347. //
  348. // Clear out the current character (which must be
  349. // a double-byte character) and then hop up one line.
  350. //
  351. ASSERT(ConsoleX == 2);
  352. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,0,ConsoleY);
  353. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,1,ConsoleY);
  354. ConsoleX = _CmdConsBlock->VideoVars->ScreenWidth-1;
  355. ConsoleY--;
  356. }
  357. } else {
  358. //
  359. // The cursor is at x=0. This can't happen if
  360. // there's a fill space at the end of the previous line,
  361. // so we don't need to worry about handling that here.
  362. //
  363. ASSERT(OemLen != 3);
  364. ConsoleX = _CmdConsBlock->VideoVars->ScreenWidth - OemLen;
  365. ConsoleY--;
  366. //
  367. // Clear out second half of double-byte char if necessary.
  368. //
  369. if(OemLen > 1) {
  370. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX+1,ConsoleY);
  371. }
  372. }
  373. s[0] = CURSOR;
  374. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  375. }
  376. ulOrigY = ConsoleY;
  377. break;
  378. case ASCI_CR:
  379. //
  380. // Erase the cursor and advance the current position one line.
  381. //
  382. s[0] = L' ';
  383. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  384. ConsoleX = 0;
  385. pRcLineDown(0);
  386. //
  387. // We know there's room in the buffer because we accounted
  388. // for the terminating nul up front.
  389. //
  390. Buffer[LineLen] = 0;
  391. Done = TRUE;
  392. break;
  393. default:
  394. //
  395. // Plain old character. Note that it could be a double-byte char,
  396. // which takes up 2 char widths on the screen.
  397. //
  398. // Also disallow control chars.
  399. //
  400. if((s[0] >= L' ') && (LineLen < MaxLineLen)) {
  401. //
  402. // Convert to OEM, including nul byte
  403. //
  404. RtlUnicodeToOemN(Oem,3,&OemLen,PasswordProtect?L"*":s,2*sizeof(WCHAR));
  405. OemLen--;
  406. //
  407. // Save the character in the caller's buffer.
  408. // Also we use the ConsoleOemString buffer as temp storage space
  409. // to store the length in oem chars of each char input.
  410. //
  411. Buffer[LineLen] = s[0];
  412. ConsoleOemString[LineLen] = (UCHAR)OemLen;
  413. //
  414. // If the character is double-byte, then there might not be
  415. // enough room on the current line for it. Check for that here.
  416. //
  417. if((ConsoleX+OemLen) > _CmdConsBlock->VideoVars->ScreenWidth) {
  418. //
  419. // Erase the cursor from the last position on the line.
  420. //
  421. s[0] = L' ';
  422. SpvidDisplayString(
  423. s,
  424. CONSOLE_ATTRIBUTE,
  425. _CmdConsBlock->VideoVars->ScreenWidth-1,
  426. ConsoleY
  427. );
  428. //
  429. // Adjust cursor position.
  430. //
  431. ConsoleX = 0;
  432. // >> ulOrigY = ConsoleY;
  433. bScrolled = FALSE;
  434. pRcLineDown(&bScrolled);
  435. //
  436. // if screen scrolled then we need to adjust original Y coordinate
  437. // appropriately
  438. //
  439. if (bScrolled && (ulOrigY > 0))
  440. --ulOrigY;
  441. //
  442. // Special handling for this case, so backspace will
  443. // work correctly.
  444. //
  445. ConsoleOemString[LineLen] = 0;
  446. }
  447. SpvidDisplayOemString(Oem,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  448. ConsoleX += OemLen;
  449. if(ConsoleX == _CmdConsBlock->VideoVars->ScreenWidth) {
  450. ConsoleX = 0;
  451. // >> ulOrigY = ConsoleY;
  452. bScrolled = FALSE;
  453. pRcLineDown(&bScrolled);
  454. //
  455. // if screen scrolled then we need to adjust original Y coordinate
  456. // appropriately
  457. //
  458. if (bScrolled && (ulOrigY > 0))
  459. --ulOrigY;
  460. }
  461. //
  462. // Now display cursor at cursor position for next character.
  463. //
  464. s[0] = CURSOR;
  465. SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  466. LineLen++;
  467. }
  468. break;
  469. }
  470. }
  471. } while(!Done);
  472. Buffer[LineLen] = 0;
  473. // save the line for future use only if its not a password
  474. if (LineLen && !PasswordProtect) {
  475. LineHistory[NextPos].Length = LineLen;
  476. wcscpy(LineHistory[NextPos].Line,Buffer);
  477. NextPos += 1;
  478. if (NextPos >= MAX_HISTORY_LINES) {
  479. NextPos = 0;
  480. }
  481. }
  482. return LineLen;
  483. }
  484. BOOLEAN
  485. RcRawTextOut(
  486. IN LPCWSTR Text,
  487. IN LONG Length
  488. )
  489. /*++
  490. Routine Description:
  491. Write a text string to the console at the current position
  492. (incidated by the ConsoleX and ConsoleY global variables). If the string
  493. is longer than will fit on the current line, it is broken up properly
  494. to span multiple lines. The screen is scrolled if necessary.
  495. This routine handles double-byte characters correctly, ensuring that
  496. no double-byte character is split across lines.
  497. Arguments:
  498. Text - supplies the text to be output. Text strings longer than
  499. ConsoleMaxOemStringLen are truncated. The string need not be
  500. nul-terminated if Length is not -1.
  501. Length - supplies the number of characters to output. If -1, then
  502. Text is assumed to be nul-terminated and the length is
  503. calculated automatically.
  504. Return Value:
  505. FALSE if we're in more mode and when prompted the user hit esc.
  506. TRUE otherwise.
  507. Upon return, the global variables ConsoleX and ConsoleY point at the next
  508. empty location on the screen.
  509. --*/
  510. {
  511. ULONG OemLen;
  512. ULONG len;
  513. ULONG i;
  514. UCHAR c;
  515. PUCHAR p;
  516. PUCHAR LastLead;
  517. BOOLEAN NewLine;
  518. BOOLEAN Dbcs;
  519. //
  520. // Convert the string to the OEM charset and determine the number
  521. // of character spaces the string will occupy on-screen, which is
  522. // equal to the number of bytes in the OEM representation of the string.
  523. // If this is not the same as the number of Unicode characters
  524. // in the string, then we've got a string with double-byte chars in it.
  525. //
  526. len = ((Length == -1) ? wcslen(Text) : Length);
  527. RtlUnicodeToOemN(
  528. ConsoleOemString,
  529. ConsoleMaxOemStringLen,
  530. &OemLen,
  531. (PVOID)Text,
  532. len * sizeof(WCHAR)
  533. );
  534. Dbcs = (OemLen != len);
  535. //
  536. // If we think we have a double-byte string, we better be prepared
  537. // to handle it properly.
  538. //
  539. if(Dbcs) {
  540. ASSERT(NLS_MB_OEM_CODE_PAGE_TAG);
  541. ASSERT(ConsoleDbcs);
  542. }
  543. //
  544. // Spit out the prompt in pieces until we've got all the characters
  545. // displayed.
  546. //
  547. ASSERT(ConsoleX < _CmdConsBlock->VideoVars->ScreenWidth);
  548. ASSERT(ConsoleY < _CmdConsBlock->VideoVars->ScreenHeight);
  549. p = ConsoleOemString;
  550. while(OemLen) {
  551. if((ConsoleX+OemLen) > _CmdConsBlock->VideoVars->ScreenWidth) {
  552. len = _CmdConsBlock->VideoVars->ScreenWidth - ConsoleX;
  553. //
  554. // Avoid splitting a double-byte character across lines.
  555. //
  556. if(Dbcs) {
  557. for(LastLead=NULL,i=0; i<len; i++) {
  558. if(IS_LEAD_BYTE(p[i])) {
  559. LastLead = &p[i];
  560. i++;
  561. }
  562. }
  563. if(LastLead == &p[len-1]) {
  564. len--;
  565. }
  566. }
  567. NewLine = TRUE;
  568. } else {
  569. //
  570. // It fits on the current line, just display it.
  571. //
  572. len = OemLen;
  573. NewLine = ((ConsoleX+len) == _CmdConsBlock->VideoVars->ScreenWidth);
  574. }
  575. c = p[len];
  576. p[len] = 0;
  577. SpvidDisplayOemString(p,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
  578. p[len] = c;
  579. p += len;
  580. OemLen -= len;
  581. if(NewLine) {
  582. ConsoleX = 0;
  583. if(!pRcLineDown(0)) {
  584. return(FALSE);
  585. }
  586. } else {
  587. ConsoleX += len;
  588. }
  589. }
  590. return(TRUE);
  591. }
  592. NTSTATUS
  593. RcBatchOut(
  594. IN PWSTR strW
  595. )
  596. {
  597. NTSTATUS Status;
  598. IO_STATUS_BLOCK IoStatusBlock;
  599. ULONG OemLen;
  600. ULONG len;
  601. len = wcslen(strW);
  602. RtlUnicodeToOemN(
  603. ConsoleOemString,
  604. ConsoleMaxOemStringLen,
  605. &len,
  606. (PVOID)strW,
  607. len * sizeof(WCHAR)
  608. );
  609. Status = ZwWriteFile(
  610. OutputFileHandle,
  611. NULL,
  612. NULL,
  613. NULL,
  614. &IoStatusBlock,
  615. (PVOID)ConsoleOemString,
  616. len,
  617. &OutputFileOffset,
  618. NULL
  619. );
  620. if (NT_SUCCESS(Status)) {
  621. OutputFileOffset.LowPart += len;
  622. }
  623. return Status;
  624. }
  625. BOOLEAN
  626. RcTextOut(
  627. IN LPCWSTR Text
  628. )
  629. {
  630. LPCWSTR p,q;
  631. if (InBatchMode && OutputFileHandle) {
  632. if (RcBatchOut( (LPWSTR)Text ) == STATUS_SUCCESS) {
  633. return TRUE;
  634. }
  635. }
  636. if (InBatchMode && RedirectToNULL) {
  637. return TRUE;
  638. }
  639. p = Text;
  640. while(*p) {
  641. //
  642. // Locate line terminator, which is cr, lf, or nul.
  643. //
  644. q = p;
  645. while(*q && (*q != L'\r') && (*q != L'\n')) {
  646. q++;
  647. }
  648. //
  649. // Print this segment out.
  650. //
  651. if(!RcRawTextOut(p,(LONG)(q-p))) {
  652. return(FALSE);
  653. }
  654. //
  655. // Handle cr's and lf's.
  656. //
  657. p = q;
  658. while((*p == L'\n') || (*p == L'\r')) {
  659. if(*p == L'\n') {
  660. if(!pRcLineDown(0)) {
  661. return(FALSE);
  662. }
  663. } else {
  664. if(*p == L'\r') {
  665. ConsoleX = 0;
  666. }
  667. }
  668. p++;
  669. }
  670. }
  671. return(TRUE);
  672. }
  673. BOOLEAN
  674. pRcLineDown(
  675. BOOLEAN *pbScrolled
  676. )
  677. {
  678. WCHAR *p;
  679. unsigned u;
  680. ULONG c;
  681. BOOLEAN b;
  682. if (pbScrolled)
  683. *pbScrolled = FALSE;
  684. b = TRUE;
  685. ConsoleY++;
  686. pMoreLinesOut++;
  687. if(ConsoleY == _CmdConsBlock->VideoVars->ScreenHeight) {
  688. //
  689. // Reached the bottom of the screen, need to scroll.
  690. //
  691. ConsoleY--;
  692. SpvidScrollUp(
  693. CONSOLE_HEADER_HEIGHT,
  694. _CmdConsBlock->VideoVars->ScreenHeight-1,
  695. 1,
  696. CONSOLE_BACKGROUND
  697. );
  698. if (pbScrolled)
  699. *pbScrolled = TRUE;
  700. }
  701. //
  702. // If we're in more mode and we've output the max number of lines
  703. // allowed before requiring user input, get that input now.
  704. //
  705. if(pMoreMode
  706. && (pMoreLinesOut == pMoreMaxLines)
  707. && (p = SpRetreiveMessageText(ImageBase,MSG_MORE_PROMPT,NULL,0))) {
  708. //
  709. // Don't bother calling the format message routine, since that
  710. // requires some other buffer. Just strip off cr/lf manually.
  711. //
  712. u = wcslen(p);
  713. while(u && ((p[u-1] == L'\r') || (p[u-1] == L'\n'))) {
  714. p[--u] = 0;
  715. }
  716. //
  717. // Display the more prompt at the bottom of the screen.
  718. //
  719. SpvidClearScreenRegion(
  720. 0,
  721. _CmdConsBlock->VideoVars->ScreenHeight - 1,
  722. _CmdConsBlock->VideoVars->ScreenWidth,
  723. 1,
  724. CONSOLE_MORE_BACKGROUND
  725. );
  726. SpvidDisplayString(
  727. p,
  728. CONSOLE_MORE_ATTRIBUTE,
  729. 2,
  730. _CmdConsBlock->VideoVars->ScreenHeight - 1
  731. );
  732. //
  733. // We don't need the prompt any more.
  734. //
  735. SpMemFree(p);
  736. //
  737. // Wait for the user to hit space, cr, or esc.
  738. //
  739. pMoreLinesOut = 0;
  740. while(1) {
  741. c = SpInputGetKeypress();
  742. if(c == ASCI_CR) {
  743. //
  744. // Allow one more line before prompting user.
  745. //
  746. pMoreMaxLines = 1;
  747. break;
  748. } else {
  749. if(c == ASCI_ESC) {
  750. //
  751. // User wants to stop the current command.
  752. //
  753. b = FALSE;
  754. break;
  755. } else {
  756. if(c == L' ') {
  757. //
  758. // Allow a whole page more.
  759. //
  760. pMoreMaxLines = _CmdConsBlock->VideoVars->ScreenHeight
  761. - (CONSOLE_HEADER_HEIGHT + 1);
  762. break;
  763. }
  764. }
  765. }
  766. }
  767. SpvidClearScreenRegion(
  768. 0,
  769. _CmdConsBlock->VideoVars->ScreenHeight - 1,
  770. _CmdConsBlock->VideoVars->ScreenWidth,
  771. 1,
  772. CONSOLE_BACKGROUND
  773. );
  774. }
  775. return(b);
  776. }
  777. VOID
  778. pRcEnableMoreMode(
  779. VOID
  780. )
  781. {
  782. pMoreMode = TRUE;
  783. pMoreLinesOut = 0;
  784. //
  785. // The maximum number of lines we allow before prompting the user
  786. // is the screen height minus the header area. We also reserve
  787. // one line for the prompt area.
  788. //
  789. pMoreMaxLines = _CmdConsBlock->VideoVars->ScreenHeight - (CONSOLE_HEADER_HEIGHT + 1);
  790. }
  791. VOID
  792. pRcDisableMoreMode(
  793. VOID
  794. )
  795. {
  796. pMoreMode = FALSE;
  797. }
  798. ULONG
  799. RcCmdCls(
  800. IN PTOKENIZED_LINE TokenizedLine
  801. )
  802. {
  803. if (RcCmdParseHelp( TokenizedLine, MSG_CLS_HELP )) {
  804. return 1;
  805. }
  806. //
  807. // Call worker routine to actually do the work
  808. //
  809. pRcCls();
  810. return 1;
  811. }
  812. VOID
  813. pRcCls(
  814. VOID
  815. )
  816. {
  817. //
  818. // Initialize location and clear screen.
  819. //
  820. ConsoleX = 0;
  821. ConsoleY = CONSOLE_HEADER_HEIGHT;
  822. SpvidClearScreenRegion(0,0,0,0,CONSOLE_BACKGROUND);
  823. }