windows-nt/Source/XPSP1/NT/base/fs/utils/more/pager.cxx

852 lines
18 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990-2000 Microsoft Corporation
Module Name:
PAGER
Abstract:
This module contains the implementations for the PAGER class
Author:
Ramon Juan San Andres (RamonSA) 15-Apr-1990
Notes:
--*/
#include "ulib.hxx"
#include "filestrm.hxx"
#include "wstring.hxx"
#include "bstring.hxx"
#include "screen.hxx"
#include "stream.hxx"
#include "more.hxx"
#include "pager.hxx"
//
// What constitutes a "blank" character (spaces and tabs)
//
#define BLANKCHARACTERS (LPWSTR)L" \t"
#define TAB_ASCII (CHAR)'\t'
DEFINE_CONSTRUCTOR( PAGER, OBJECT );
VOID
PAGER::Construct (
)
{
UNREFERENCED_PARAMETER( this );
}
PAGER::~PAGER (
)
{
DELETE( _String );
DELETE( _BString );
DELETE( _Blanks );
DELETE( _BlankLine );
}
BOOLEAN
PAGER::Initialize (
IN PSTREAM Stream,
IN PPROGRAM Program
)
/*++
Routine Description:
Phase 2 of construction for a pager object. It initializes its
internal data.
Arguments:
Stream - Supplies the stream to be paged
Program - Supplies pointer to the program doing the paging.
Return Value:
TRUE - If initialized correctly
FALSE - If something when wrong. (check error stack)
--*/
{
USHORT ScreenRows, RowsInPage;
CHNUM i;
_Stream = Stream;
_CurrentLineNumber = 0;
_StandardOutput = Program->GetStandardOutput();
_Screen = SCREEN::Cast( _StandardOutput );
_Position = INVALID_CHNUM;
//
// Get the page dimensions
//
if (_Screen) {
_Screen->QueryScreenSize( &ScreenRows, &_ColumnsInScreen, &RowsInPage, &_ColumnsInPage );
_RowsInPage = RowsInPage;
_ColumnsInPage = _ColumnsInScreen;
} else {
// If we don't have a screen then we should treat the page
// as infinitely long since we have no need to prompt.
_RowsInPage = (ULONG) -1;
_ColumnsInPage = (USHORT) -1;
}
if (!(_String = NEW DSTRING) ||
!(_BString = NEW BDSTRING) ||
!(_Blanks = NEW DSTRING) ||
!(_BlankLine = NEW DSTRING) ||
!_String->Initialize() ||
!_BString->Initialize() ||
!_Blanks->Initialize(BLANKCHARACTERS) ||
!_BlankLine->Resize(_Screen ? _ColumnsInPage : 0)) {
return FALSE;
}
for (i = 0; i < _BlankLine->QueryChCount(); i++) {
_BlankLine->SetChAt(' ', i);
}
return TRUE;
}
BOOLEAN
PAGER::DisplayPage (
IN ULONG LinesInPage,
IN BOOLEAN ClearScreen,
IN BOOLEAN SqueezeBlankLines,
IN BOOLEAN ExpandFormFeed,
IN ULONG TabExp
)
/*++
Routine Description:
Displays a page of the screen
Arguments:
LinesInPage - Supplies the desired number of lines in the page
ClearScreen - Supplies a flag, which if TRUE means that we
want to clear the screen before displaying the
page
SqueezeBlankLines - Supplies squeeze flag
ExpandFormFeed - Supplies formfeed expansion flag
Return Value:
TRUE - If page displayed
FALSE otherwise
--*/
{
ULONG LinesLeft = LinesInPage;
BOOLEAN IgnoreBlankLine = FALSE;
CHNUM Length;
CHNUM FormFeedAt;
if ( TabExp > (ULONG)( _ColumnsInPage - 1 ) ) {
TabExp = _ColumnsInPage - 1 ;
}
//
// Clear the screen if instructed to do so
//
if ( ClearScreen && _Screen) {
#ifdef FE_SB // v-junm - 08/18/93
// Also reset attributes.
_Screen->EraseScreenAndResetAttribute();
#else
_Screen->EraseScreen();
#endif
_Screen->MoveCursorTo( 0, 0 );
} else {
//
// Make sure that we start at the beginning of a line
//
ClearLine();
}
//
// Display up to LinesLeft lines
//
while ( LinesLeft > 0 &&
(ThereIsMoreToPage() || _Position != INVALID_CHNUM) ) {
//
// Get next string from input
//
if (!ReadNextString( TabExp )) {
return FALSE;
}
#ifdef FE_SB // v-junm - 08/18/93
// Now QueryChCount returns the correct number of unicode chars. An additional
// API, QueryByteCount was added.
Length = _String->QueryByteCount() - _Position;
#else
Length = _String->QueryChCount() - _Position;
#endif
if ( SqueezeBlankLines ) {
if ( _String->Strspn( _Blanks, _Position ) == INVALID_CHNUM ) {
//
// This is a blank line. We must sqeeze
//
if ( IgnoreBlankLine ) {
//
// We ignore the line
//
_Position = INVALID_CHNUM;
continue;
} else {
//
// We will print a blank line and ignore the following
// blank lines.
//
DisplayBlankLine( 1 );
_Position = INVALID_CHNUM;
IgnoreBlankLine = TRUE;
continue;
}
} else if (IgnoreBlankLine) {
LinesLeft--;
IgnoreBlankLine = FALSE;
continue;
}
}
if ( ExpandFormFeed ) {
//
// Look for form feed within line
//
if ((FormFeedAt = _String->Strchr( FORMFEED,_Position )) != INVALID_CHNUM) {
if ( FormFeedAt == _Position ) {
//
// First character is a form feed.
//
// We will skip the formfeed character, and the
// rest of the screen will be blanked.
//
if ( SqueezeBlankLines ) {
if (!IgnoreBlankLine) {
DisplayBlankLine( 1 );
LinesLeft--;
IgnoreBlankLine = TRUE;
}
} else {
DisplayBlankLine( LinesLeft );
LinesLeft = 0;
}
_Position++;
continue;
}
Length = FormFeedAt - _Position;
}
}
//
// If the line is too long, we must split it
//
if (Length > (CHNUM)_ColumnsInPage) {
Length = (CHNUM)_ColumnsInPage;
}
//
// Display the string
//
DisplayString( _String, &_Position, Length );
IgnoreBlankLine = FALSE;
LinesLeft--;
}
return TRUE;
}
VOID
PAGER::ClearLine (
)
/*++
Routine Description:
Clears a line
Arguments:
none
Return Value:
none
--*/
{
USHORT ScreenRows, ScreenCols;
USHORT WindowRows, WindowCols;
CHNUM i;
if (_Screen) {
_Screen->QueryScreenSize( &ScreenRows,
&ScreenCols,
&WindowRows,
&WindowCols );
//
// If the number of columns has changed, re-initialize the
// blank line.
//
if ( ScreenCols != _ColumnsInPage ) {
_BlankLine->Resize(ScreenCols);
for (i = 0; i < ScreenCols; i++) {
_BlankLine->SetChAt(' ', i);
}
}
_RowsInPage = WindowRows;
_ColumnsInPage = ScreenCols;
_StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
_StandardOutput->WriteString( _BlankLine, 0, _ColumnsInPage-1 );
_StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
}
}
VOID
PAGER::DisplayBlankLine (
IN ULONG Lines,
IN BOOLEAN NewLine
)
/*++
Routine Description:
Displays a number of blank lines
Arguments:
Lines - Supplies the number of blank lines to display
NewLine - Supplies the newline flag
Return Value:
none
--*/
{
CHNUM Position;
while ( Lines-- ) {
Position = 0;
DisplayString( _BlankLine, &Position, _ColumnsInPage-1, (Lines > 0) ? TRUE : NewLine);
}
}
VOID
PAGER::DisplayString (
IN PWSTRING String,
OUT PCHNUM Position,
IN CHNUM Length,
IN BOOLEAN NewLine
)
/*++
Routine Description:
Displays a chunk of the current string
Arguments:
Length - Supplies the length of the string to display
NewLine - Supplies the newline flag
Return Value:
none
--*/
{
#ifdef FE_SB // v-junm - 04/19/93
CHNUM UnicodeCharNum, // # of unicode characters to display
TempByteCount, // to minimize calls to QueryByteCount
index; // loop counter
PSTR STRBuffer; // ASCIIZ converted string pointer
static CHNUM OldPos; // Keeps old position in # of Unicode
BOOL DBCSFlag = FALSE; // Flag for ending leadbyte
TempByteCount = String->QueryByteCount();
Length = min( Length, TempByteCount );
// If the length of the string to display is shorter than
// the width of the screen, we do not have to do any DBCS
// checking. Just print it out. (Can be skipped for CP437)
//
if ( TempByteCount > _ColumnsInPage ) {
//
// Initialize # of unicode characters to #
// of ASCII chars to display.
//
UnicodeCharNum = Length;
//
// Get the string as a ASCIIZ text.
//
STRBuffer = String->QuerySTR();
if ( STRBuffer != NULL ) {
//
// Start changing the UnicodeCharNum from the actual
// number of bytes to the actual number of characters.
//
for( index = 0; index < Length; index++ ) {
if ( IsLeadByte( *(STRBuffer + index + _Position) ) ) {
index++;
UnicodeCharNum--;
DBCSFlag = TRUE;
}
else
DBCSFlag = FALSE;
}
DELETE( STRBuffer );
}
//
// If the following conditions are true, then there
// is a Leadbyte at the end of the screen that needs
// to be displayed on the next line with it's tail byte.
//
if ( DBCSFlag == TRUE && // String ends with DBCS.
index == (Length - 1) && // Only Leadbyte.
Length == (CHNUM)_ColumnsInPage // More rows to display.
) {
Length--;
UnicodeCharNum--;
}
}
else
//fix kksuzuka: #195
//Overflow pagecolumns when writing 0D0A.
//UnicodeCharNum = String->QueryChCount();
UnicodeCharNum = min( Length, String->QueryChCount() );
//
// When the string does not fit on one line and needs to be truncated,
// OldPos keeps the position where the string was truncated in Unicode
// location.
//
//
// If true, set to beginning of string.
//
if ( *Position == 0 )
OldPos = 0;
_StandardOutput->WriteString( String, OldPos, UnicodeCharNum );
//
// Set to last+1 char displayed in unicode character numbers.
//
OldPos += UnicodeCharNum;
//
// Update our position within the string
//
*Position += Length;
//
// Check if all the characters have been written.
//
if ( TempByteCount && (TempByteCount == *Position) &&
!(TempByteCount % _ColumnsInPage) ) {
//
// Characters have been written, but there is a LF/CR
// character at the that needs to be display that has
// not been displayed. (At _ColumnsInPage+1 location)
//
*Position = INVALID_CHNUM;
// _StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
// _StandardOutput->WriteChar( (WCHAR)LINEFEED );
}
else if ( *Position >= TempByteCount )
*Position = INVALID_CHNUM;
if ( ((*Position != INVALID_CHNUM) || NewLine) &&
( Length < _ColumnsInScreen || !_Screen ) ) {
_StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
_StandardOutput->WriteChar( (WCHAR)LINEFEED );
}
#else // FE_SB
Length = min( Length, String->QueryChCount() );
_StandardOutput->WriteString( String, *Position, Length );
//
// Update our position within the string
//
*Position += Length;
if (*Position >= _String->QueryChCount()) {
*Position = INVALID_CHNUM;
}
if ( ((*Position != INVALID_CHNUM) || NewLine) &&
( Length < _ColumnsInScreen || !_Screen ) ) {
_StandardOutput->WriteChar( (WCHAR)CARRIAGERETURN );
_StandardOutput->WriteChar( (WCHAR)LINEFEED );
}
#endif
}
ULONGLONG
PAGER::QueryCurrentByte (
)
/*++
Routine Description:
Queries the current Byte number
Arguments:
none
Return Value:
The current byte number
--*/
{
PFILE_STREAM pFileStream;
ULONGLONG PointerPosition ;
if ((pFileStream = FILE_STREAM::Cast(_Stream)) == NULL ) {
return 0;
} else {
pFileStream->QueryPointerPosition( &PointerPosition );
return PointerPosition;
}
}
USHORT
PAGER::QueryLinesPerPage (
)
/*++
Routine Description:
Queries the number of lines per page of output
Arguments:
none
Return Value:
The number of lines (rows) in a page
--*/
{
USHORT ScreenRows, ScreenCols;
USHORT WindowRows, WindowCols;
CHNUM i;
//
// If Paging to screen, get the current size of the window
//
if (_Screen) {
_Screen->QueryScreenSize( &ScreenRows,
&ScreenCols,
&WindowRows,
&WindowCols );
//
// If the number of columns has changed, re-initialize the
// blank line.
//
if ( WindowCols != _ColumnsInPage ) {
_BlankLine->Resize(ScreenCols);
for (i = 0; i < ScreenCols; i++) {
_BlankLine->SetChAt(' ', i);
}
}
_RowsInPage = WindowRows;
_ColumnsInPage = ScreenCols;
}
return (USHORT)_RowsInPage;
}
BOOLEAN
PAGER::ReadNextString (
IN ULONG TabExp
)
/*++
Routine Description:
Reads in the next string from the input stream.
Arguments:
TabExp - Supplies the number of blank characters per tab
Return Value:
TRUE - If string read in
FALSE otherwise
--*/
{
if (_Position == INVALID_CHNUM ) {
CHNUM Idx;
//
// We have to read a new string from the input stream.
//
if (!_Stream->ReadLine( _String )) {
return FALSE;
}
_CurrentLineNumber++;
_Position = 0;
//
// Expand tabs
//
Idx = 0;
//
// Get the string as a ASCIIZ text.
//
PSTR szBuffer; // ASCIIZ converted string pointer
szBuffer = _String->QuerySTR();
if (szBuffer != NULL) {
_BString->Initialize(szBuffer);
DELETE( szBuffer );
while ( (Idx < _BString->QueryChCount()) &&
((Idx = _BString->Strchr( TAB_ASCII, Idx )) != INVALID_CHNUM) ) {
if (TabExp) {
_BString->ReplaceWithChars(
Idx, // AtPosition
1, // AtLength
' ', // Replacement char
TabExp - Idx%TabExp // FromLength
);
} else {
_BString->ReplaceWithChars(
Idx, // AtPosition
1, // AtLength
' ', // Replacement char
0 // FromLength
);
}
//
// MJB: If we're eliminating tabs we don't want to advance the
// index; removing the previous tab has pulled the next character
// in *to* the index, so it's already where we want it. Advancing
// regardless can cause every other adjacent tab not to be
// elminiated.
//
if (TabExp > 0) {
Idx = _BString->NextChar(Idx);
}
}
szBuffer = _BString->QuerySTR();
if (szBuffer != NULL) {
_String->Initialize(szBuffer);
DELETE( szBuffer );
}
}
}
return TRUE;
}
BOOLEAN
PAGER::SkipLines (
IN ULONG LinesToSkip,
IN ULONG TabExp
)
/*++
Routine Description:
Skips certain number of lines
Arguments:
LinesToSkip - Supplies the number of lines to skip
TabExp - Supplies number of spaces per tab
Return Value:
TRUE - If lines skipped
FALSE otherwise
--*/
{
if ( TabExp > (ULONG)( _ColumnsInPage - 1 ) ) {
TabExp = _ColumnsInPage - 1;
}
while ( LinesToSkip-- && ThereIsMoreToPage() ) {
if (!ReadNextString( TabExp )) {
return FALSE;
}
_Position = INVALID_CHNUM;
}
return TRUE;
}
#ifdef FE_SB // v-junm - 09/24/93
BOOLEAN
PAGER::IsLeadByte(
IN BYTE c
)
/*++
Routine Description:
Checks to see if c is a leadbyte of a DBCS character.
Arguments:
c - character to check to see if leadbyte.
Return Value:
TRUE - leadbyte of DBCS character.
FALSE otherwise
--*/
{
CPINFO cp;
static UINT outputcp = GetConsoleOutputCP();
int i;
if ( GetCPInfo( outputcp, &cp ) ) {
//
// Code page info has been aquired. From code page info,
// the leadbyte range can be determined.
//
for( i = 0; cp.LeadByte[i] && cp.LeadByte[i+1]; i += 2 ) {
//
// There are leadbytes. Check to see if c falls in
// current leadbyte range.
//
if ( c >= cp.LeadByte[i] && c <= cp.LeadByte[i+1] )
return( TRUE );
}
return( FALSE );
}
else {
//
// This will not produce correct results if
// 'ConsoleOutputCP != SystemCP && DBCS System'
// Just making system conversion the default
// when GetCPInfo doesn't work.
//
return( IsDBCSLeadByte( c ) != FALSE );
}
}
#endif