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.

851 lines
18 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. PAGER
  5. Abstract:
  6. This module contains the implementations for the PAGER class
  7. Author:
  8. Ramon Juan San Andres (RamonSA) 15-Apr-1990
  9. Notes:
  10. --*/
  11. #include "ulib.hxx"
  12. #include "filestrm.hxx"
  13. #include "wstring.hxx"
  14. #include "bstring.hxx"
  15. #include "screen.hxx"
  16. #include "stream.hxx"
  17. #include "more.hxx"
  18. #include "pager.hxx"
  19. //
  20. // What constitutes a "blank" character (spaces and tabs)
  21. //
  22. #define BLANKCHARACTERS (LPWSTR)L" \t"
  23. #define TAB_ASCII (CHAR)'\t'
  24. DEFINE_CONSTRUCTOR( PAGER, OBJECT );
  25. VOID
  26. PAGER::Construct (
  27. )
  28. {
  29. UNREFERENCED_PARAMETER( this );
  30. }
  31. PAGER::~PAGER (
  32. )
  33. {
  34. DELETE( _String );
  35. DELETE( _BString );
  36. DELETE( _Blanks );
  37. DELETE( _BlankLine );
  38. }
  39. BOOLEAN
  40. PAGER::Initialize (
  41. IN PSTREAM Stream,
  42. IN PPROGRAM Program
  43. )
  44. /*++
  45. Routine Description:
  46. Phase 2 of construction for a pager object. It initializes its
  47. internal data.
  48. Arguments:
  49. Stream - Supplies the stream to be paged
  50. Program - Supplies pointer to the program doing the paging.
  51. Return Value:
  52. TRUE - If initialized correctly
  53. FALSE - If something when wrong. (check error stack)
  54. --*/
  55. {
  56. USHORT ScreenRows, RowsInPage;
  57. CHNUM i;
  58. _Stream = Stream;
  59. _CurrentLineNumber = 0;
  60. _StandardOutput = Program->GetStandardOutput();
  61. _Screen = SCREEN::Cast( _StandardOutput );
  62. _Position = INVALID_CHNUM;
  63. //
  64. // Get the page dimensions
  65. //
  66. if (_Screen) {
  67. _Screen->QueryScreenSize( &ScreenRows, &_ColumnsInScreen, &RowsInPage, &_ColumnsInPage );
  68. _RowsInPage = RowsInPage;
  69. _ColumnsInPage = _ColumnsInScreen;
  70. } else {
  71. // If we don't have a screen then we should treat the page
  72. // as infinitely long since we have no need to prompt.
  73. _RowsInPage = (ULONG) -1;
  74. _ColumnsInPage = (USHORT) -1;
  75. }
  76. if (!(_String = NEW DSTRING) ||
  77. !(_BString = NEW BDSTRING) ||
  78. !(_Blanks = NEW DSTRING) ||
  79. !(_BlankLine = NEW DSTRING) ||
  80. !_String->Initialize() ||
  81. !_BString->Initialize() ||
  82. !_Blanks->Initialize(BLANKCHARACTERS) ||
  83. !_BlankLine->Resize(_Screen ? _ColumnsInPage : 0)) {
  84. return FALSE;
  85. }
  86. for (i = 0; i < _BlankLine->QueryChCount(); i++) {
  87. _BlankLine->SetChAt(' ', i);
  88. }
  89. return TRUE;
  90. }
  91. BOOLEAN
  92. PAGER::DisplayPage (
  93. IN ULONG LinesInPage,
  94. IN BOOLEAN ClearScreen,
  95. IN BOOLEAN SqueezeBlankLines,
  96. IN BOOLEAN ExpandFormFeed,
  97. IN ULONG TabExp
  98. )
  99. /*++
  100. Routine Description:
  101. Displays a page of the screen
  102. Arguments:
  103. LinesInPage - Supplies the desired number of lines in the page
  104. ClearScreen - Supplies a flag, which if TRUE means that we
  105. want to clear the screen before displaying the
  106. page
  107. SqueezeBlankLines - Supplies squeeze flag
  108. ExpandFormFeed - Supplies formfeed expansion flag
  109. Return Value:
  110. TRUE - If page displayed
  111. FALSE otherwise
  112. --*/
  113. {
  114. ULONG LinesLeft = LinesInPage;
  115. BOOLEAN IgnoreBlankLine = FALSE;
  116. CHNUM Length;
  117. CHNUM FormFeedAt;
  118. if ( TabExp > (ULONG)( _ColumnsInPage - 1 ) ) {
  119. TabExp = _ColumnsInPage - 1 ;
  120. }
  121. //
  122. // Clear the screen if instructed to do so
  123. //
  124. if ( ClearScreen && _Screen) {
  125. #ifdef FE_SB // v-junm - 08/18/93
  126. // Also reset attributes.
  127. _Screen->EraseScreenAndResetAttribute();
  128. #else
  129. _Screen->EraseScreen();
  130. #endif
  131. _Screen->MoveCursorTo( 0, 0 );
  132. } else {
  133. //
  134. // Make sure that we start at the beginning of a line
  135. //
  136. ClearLine();
  137. }
  138. //
  139. // Display up to LinesLeft lines
  140. //
  141. while ( LinesLeft > 0 &&
  142. (ThereIsMoreToPage() || _Position != INVALID_CHNUM) ) {
  143. //
  144. // Get next string from input
  145. //
  146. if (!ReadNextString( TabExp )) {
  147. return FALSE;
  148. }
  149. #ifdef FE_SB // v-junm - 08/18/93
  150. // Now QueryChCount returns the correct number of unicode chars. An additional
  151. // API, QueryByteCount was added.
  152. Length = _String->QueryByteCount() - _Position;
  153. #else
  154. Length = _String->QueryChCount() - _Position;
  155. #endif
  156. if ( SqueezeBlankLines ) {
  157. if ( _String->Strspn( _Blanks, _Position ) == INVALID_CHNUM ) {
  158. //
  159. // This is a blank line. We must sqeeze
  160. //
  161. if ( IgnoreBlankLine ) {
  162. //
  163. // We ignore the line
  164. //
  165. _Position = INVALID_CHNUM;
  166. continue;
  167. } else {
  168. //
  169. // We will print a blank line and ignore the following
  170. // blank lines.
  171. //
  172. DisplayBlankLine( 1 );
  173. _Position = INVALID_CHNUM;
  174. IgnoreBlankLine = TRUE;
  175. continue;
  176. }
  177. } else if (IgnoreBlankLine) {
  178. LinesLeft--;
  179. IgnoreBlankLine = FALSE;
  180. continue;
  181. }
  182. }
  183. if ( ExpandFormFeed ) {
  184. //
  185. // Look for form feed within line
  186. //
  187. if ((FormFeedAt = _String->Strchr( FORMFEED,_Position )) != INVALID_CHNUM) {
  188. if ( FormFeedAt == _Position ) {
  189. //
  190. // First character is a form feed.
  191. //
  192. // We will skip the formfeed character, and the
  193. // rest of the screen will be blanked.
  194. //
  195. if ( SqueezeBlankLines ) {
  196. if (!IgnoreBlankLine) {
  197. DisplayBlankLine( 1 );
  198. LinesLeft--;
  199. IgnoreBlankLine = TRUE;
  200. }
  201. } else {
  202. DisplayBlankLine( LinesLeft );
  203. LinesLeft = 0;
  204. }
  205. _Position++;
  206. continue;
  207. }
  208. Length = FormFeedAt - _Position;
  209. }
  210. }
  211. //
  212. // If the line is too long, we must split it
  213. //
  214. if (Length > (CHNUM)_ColumnsInPage) {
  215. Length = (CHNUM)_ColumnsInPage;
  216. }
  217. //
  218. // Display the string
  219. //
  220. DisplayString( _String, &_Position, Length );
  221. IgnoreBlankLine = FALSE;
  222. LinesLeft--;
  223. }
  224. return TRUE;
  225. }
  226. VOID
  227. PAGER::ClearLine (
  228. )
  229. /*++
  230. Routine Description:
  231. Clears a line
  232. Arguments:
  233. none
  234. Return Value:
  235. none
  236. --*/
  237. {
  238. USHORT ScreenRows, ScreenCols;
  239. USHORT WindowRows, WindowCols;
  240. CHNUM i;
  241. if (_Screen) {
  242. _Screen->QueryScreenSize( &ScreenRows,
  243. &ScreenCols,
  244. &WindowRows,
  245. &WindowCols );
  246. //
  247. // If the number of columns has changed, re-initialize the
  248. // blank line.
  249. //
  250. if ( ScreenCols != _ColumnsInPage ) {
  251. _BlankLine->Resize(ScreenCols);
  252. for (i = 0; i < ScreenCols; i++) {
  253. _BlankLine->SetChAt(' ', i);
  254. }
  255. }
  256. _RowsInPage = WindowRows;
  257. _ColumnsInPage = ScreenCols;
  258. _StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
  259. _StandardOutput->WriteString( _BlankLine, 0, _ColumnsInPage-1 );
  260. _StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
  261. }
  262. }
  263. VOID
  264. PAGER::DisplayBlankLine (
  265. IN ULONG Lines,
  266. IN BOOLEAN NewLine
  267. )
  268. /*++
  269. Routine Description:
  270. Displays a number of blank lines
  271. Arguments:
  272. Lines - Supplies the number of blank lines to display
  273. NewLine - Supplies the newline flag
  274. Return Value:
  275. none
  276. --*/
  277. {
  278. CHNUM Position;
  279. while ( Lines-- ) {
  280. Position = 0;
  281. DisplayString( _BlankLine, &Position, _ColumnsInPage-1, (Lines > 0) ? TRUE : NewLine);
  282. }
  283. }
  284. VOID
  285. PAGER::DisplayString (
  286. IN PWSTRING String,
  287. OUT PCHNUM Position,
  288. IN CHNUM Length,
  289. IN BOOLEAN NewLine
  290. )
  291. /*++
  292. Routine Description:
  293. Displays a chunk of the current string
  294. Arguments:
  295. Length - Supplies the length of the string to display
  296. NewLine - Supplies the newline flag
  297. Return Value:
  298. none
  299. --*/
  300. {
  301. #ifdef FE_SB // v-junm - 04/19/93
  302. CHNUM UnicodeCharNum, // # of unicode characters to display
  303. TempByteCount, // to minimize calls to QueryByteCount
  304. index; // loop counter
  305. PSTR STRBuffer; // ASCIIZ converted string pointer
  306. static CHNUM OldPos; // Keeps old position in # of Unicode
  307. BOOL DBCSFlag = FALSE; // Flag for ending leadbyte
  308. TempByteCount = String->QueryByteCount();
  309. Length = min( Length, TempByteCount );
  310. // If the length of the string to display is shorter than
  311. // the width of the screen, we do not have to do any DBCS
  312. // checking. Just print it out. (Can be skipped for CP437)
  313. //
  314. if ( TempByteCount > _ColumnsInPage ) {
  315. //
  316. // Initialize # of unicode characters to #
  317. // of ASCII chars to display.
  318. //
  319. UnicodeCharNum = Length;
  320. //
  321. // Get the string as a ASCIIZ text.
  322. //
  323. STRBuffer = String->QuerySTR();
  324. if ( STRBuffer != NULL ) {
  325. //
  326. // Start changing the UnicodeCharNum from the actual
  327. // number of bytes to the actual number of characters.
  328. //
  329. for( index = 0; index < Length; index++ ) {
  330. if ( IsLeadByte( *(STRBuffer + index + _Position) ) ) {
  331. index++;
  332. UnicodeCharNum--;
  333. DBCSFlag = TRUE;
  334. }
  335. else
  336. DBCSFlag = FALSE;
  337. }
  338. DELETE( STRBuffer );
  339. }
  340. //
  341. // If the following conditions are true, then there
  342. // is a Leadbyte at the end of the screen that needs
  343. // to be displayed on the next line with it's tail byte.
  344. //
  345. if ( DBCSFlag == TRUE && // String ends with DBCS.
  346. index == (Length - 1) && // Only Leadbyte.
  347. Length == (CHNUM)_ColumnsInPage // More rows to display.
  348. ) {
  349. Length--;
  350. UnicodeCharNum--;
  351. }
  352. }
  353. else
  354. //fix kksuzuka: #195
  355. //Overflow pagecolumns when writing 0D0A.
  356. //UnicodeCharNum = String->QueryChCount();
  357. UnicodeCharNum = min( Length, String->QueryChCount() );
  358. //
  359. // When the string does not fit on one line and needs to be truncated,
  360. // OldPos keeps the position where the string was truncated in Unicode
  361. // location.
  362. //
  363. //
  364. // If true, set to beginning of string.
  365. //
  366. if ( *Position == 0 )
  367. OldPos = 0;
  368. _StandardOutput->WriteString( String, OldPos, UnicodeCharNum );
  369. //
  370. // Set to last+1 char displayed in unicode character numbers.
  371. //
  372. OldPos += UnicodeCharNum;
  373. //
  374. // Update our position within the string
  375. //
  376. *Position += Length;
  377. //
  378. // Check if all the characters have been written.
  379. //
  380. if ( TempByteCount && (TempByteCount == *Position) &&
  381. !(TempByteCount % _ColumnsInPage) ) {
  382. //
  383. // Characters have been written, but there is a LF/CR
  384. // character at the that needs to be display that has
  385. // not been displayed. (At _ColumnsInPage+1 location)
  386. //
  387. *Position = INVALID_CHNUM;
  388. // _StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
  389. // _StandardOutput->WriteChar( (WCHAR)LINEFEED );
  390. }
  391. else if ( *Position >= TempByteCount )
  392. *Position = INVALID_CHNUM;
  393. if ( ((*Position != INVALID_CHNUM) || NewLine) &&
  394. ( Length < _ColumnsInScreen || !_Screen ) ) {
  395. _StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
  396. _StandardOutput->WriteChar( (WCHAR)LINEFEED );
  397. }
  398. #else // FE_SB
  399. Length = min( Length, String->QueryChCount() );
  400. _StandardOutput->WriteString( String, *Position, Length );
  401. //
  402. // Update our position within the string
  403. //
  404. *Position += Length;
  405. if (*Position >= _String->QueryChCount()) {
  406. *Position = INVALID_CHNUM;
  407. }
  408. if ( ((*Position != INVALID_CHNUM) || NewLine) &&
  409. ( Length < _ColumnsInScreen || !_Screen ) ) {
  410. _StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
  411. _StandardOutput->WriteChar( (WCHAR)LINEFEED );
  412. }
  413. #endif
  414. }
  415. ULONGLONG
  416. PAGER::QueryCurrentByte (
  417. )
  418. /*++
  419. Routine Description:
  420. Queries the current Byte number
  421. Arguments:
  422. none
  423. Return Value:
  424. The current byte number
  425. --*/
  426. {
  427. PFILE_STREAM pFileStream;
  428. ULONGLONG PointerPosition ;
  429. if ((pFileStream = FILE_STREAM::Cast(_Stream)) == NULL ) {
  430. return 0;
  431. } else {
  432. pFileStream->QueryPointerPosition( &PointerPosition );
  433. return PointerPosition;
  434. }
  435. }
  436. USHORT
  437. PAGER::QueryLinesPerPage (
  438. )
  439. /*++
  440. Routine Description:
  441. Queries the number of lines per page of output
  442. Arguments:
  443. none
  444. Return Value:
  445. The number of lines (rows) in a page
  446. --*/
  447. {
  448. USHORT ScreenRows, ScreenCols;
  449. USHORT WindowRows, WindowCols;
  450. CHNUM i;
  451. //
  452. // If Paging to screen, get the current size of the window
  453. //
  454. if (_Screen) {
  455. _Screen->QueryScreenSize( &ScreenRows,
  456. &ScreenCols,
  457. &WindowRows,
  458. &WindowCols );
  459. //
  460. // If the number of columns has changed, re-initialize the
  461. // blank line.
  462. //
  463. if ( WindowCols != _ColumnsInPage ) {
  464. _BlankLine->Resize(ScreenCols);
  465. for (i = 0; i < ScreenCols; i++) {
  466. _BlankLine->SetChAt(' ', i);
  467. }
  468. }
  469. _RowsInPage = WindowRows;
  470. _ColumnsInPage = ScreenCols;
  471. }
  472. return (USHORT)_RowsInPage;
  473. }
  474. BOOLEAN
  475. PAGER::ReadNextString (
  476. IN ULONG TabExp
  477. )
  478. /*++
  479. Routine Description:
  480. Reads in the next string from the input stream.
  481. Arguments:
  482. TabExp - Supplies the number of blank characters per tab
  483. Return Value:
  484. TRUE - If string read in
  485. FALSE otherwise
  486. --*/
  487. {
  488. if (_Position == INVALID_CHNUM ) {
  489. CHNUM Idx;
  490. //
  491. // We have to read a new string from the input stream.
  492. //
  493. if (!_Stream->ReadLine( _String )) {
  494. return FALSE;
  495. }
  496. _CurrentLineNumber++;
  497. _Position = 0;
  498. //
  499. // Expand tabs
  500. //
  501. Idx = 0;
  502. //
  503. // Get the string as a ASCIIZ text.
  504. //
  505. PSTR szBuffer; // ASCIIZ converted string pointer
  506. szBuffer = _String->QuerySTR();
  507. if (szBuffer != NULL) {
  508. _BString->Initialize(szBuffer);
  509. DELETE( szBuffer );
  510. while ( (Idx < _BString->QueryChCount()) &&
  511. ((Idx = _BString->Strchr( TAB_ASCII, Idx )) != INVALID_CHNUM) ) {
  512. if (TabExp) {
  513. _BString->ReplaceWithChars(
  514. Idx, // AtPosition
  515. 1, // AtLength
  516. ' ', // Replacement char
  517. TabExp - Idx%TabExp // FromLength
  518. );
  519. } else {
  520. _BString->ReplaceWithChars(
  521. Idx, // AtPosition
  522. 1, // AtLength
  523. ' ', // Replacement char
  524. 0 // FromLength
  525. );
  526. }
  527. //
  528. // MJB: If we're eliminating tabs we don't want to advance the
  529. // index; removing the previous tab has pulled the next character
  530. // in *to* the index, so it's already where we want it. Advancing
  531. // regardless can cause every other adjacent tab not to be
  532. // elminiated.
  533. //
  534. if (TabExp > 0) {
  535. Idx = _BString->NextChar(Idx);
  536. }
  537. }
  538. szBuffer = _BString->QuerySTR();
  539. if (szBuffer != NULL) {
  540. _String->Initialize(szBuffer);
  541. DELETE( szBuffer );
  542. }
  543. }
  544. }
  545. return TRUE;
  546. }
  547. BOOLEAN
  548. PAGER::SkipLines (
  549. IN ULONG LinesToSkip,
  550. IN ULONG TabExp
  551. )
  552. /*++
  553. Routine Description:
  554. Skips certain number of lines
  555. Arguments:
  556. LinesToSkip - Supplies the number of lines to skip
  557. TabExp - Supplies number of spaces per tab
  558. Return Value:
  559. TRUE - If lines skipped
  560. FALSE otherwise
  561. --*/
  562. {
  563. if ( TabExp > (ULONG)( _ColumnsInPage - 1 ) ) {
  564. TabExp = _ColumnsInPage - 1;
  565. }
  566. while ( LinesToSkip-- && ThereIsMoreToPage() ) {
  567. if (!ReadNextString( TabExp )) {
  568. return FALSE;
  569. }
  570. _Position = INVALID_CHNUM;
  571. }
  572. return TRUE;
  573. }
  574. #ifdef FE_SB // v-junm - 09/24/93
  575. BOOLEAN
  576. PAGER::IsLeadByte(
  577. IN BYTE c
  578. )
  579. /*++
  580. Routine Description:
  581. Checks to see if c is a leadbyte of a DBCS character.
  582. Arguments:
  583. c - character to check to see if leadbyte.
  584. Return Value:
  585. TRUE - leadbyte of DBCS character.
  586. FALSE otherwise
  587. --*/
  588. {
  589. CPINFO cp;
  590. static UINT outputcp = GetConsoleOutputCP();
  591. int i;
  592. if ( GetCPInfo( outputcp, &cp ) ) {
  593. //
  594. // Code page info has been aquired. From code page info,
  595. // the leadbyte range can be determined.
  596. //
  597. for( i = 0; cp.LeadByte[i] && cp.LeadByte[i+1]; i += 2 ) {
  598. //
  599. // There are leadbytes. Check to see if c falls in
  600. // current leadbyte range.
  601. //
  602. if ( c >= cp.LeadByte[i] && c <= cp.LeadByte[i+1] )
  603. return( TRUE );
  604. }
  605. return( FALSE );
  606. }
  607. else {
  608. //
  609. // This will not produce correct results if
  610. // 'ConsoleOutputCP != SystemCP && DBCS System'
  611. // Just making system conversion the default
  612. // when GetCPInfo doesn't work.
  613. //
  614. return( IsDBCSLeadByte( c ) != FALSE );
  615. }
  616. }
  617. #endif