|
|
/*++
Copyright (c) 1990-2000 Microsoft Corporation
Module Name:
More.cxx
Abstract:
"More" pager
Author:
Ramon Juan San Andres (ramonsa) 11-Apr-1990
Revision History:
--*/
#include "ulib.hxx"
#include "arg.hxx"
#include "arrayit.hxx"
#include "file.hxx"
#include "filestrm.hxx"
#include "keyboard.hxx"
#include "rtmsg.h"
#include "pager.hxx"
#include "path.hxx"
#include "smsg.hxx"
#include "system.hxx"
#include "more.hxx"
#define DEFAULT_TABEXP 8
#define NULL_CHARACTER ((CHAR)'\0')
#define CTRLC_CHARACTER ((CHAR)0x03)
VOID __cdecl main ( )
/*++
Routine Description:
Main function of the more pager.
Arguments:
None.
Return Value:
None.
Notes:
--*/
{
// Initialize stuff
//
DEFINE_CLASS_DESCRIPTOR( MORE );
//
// Now do the paging
//
{ MORE More;
//
// Initialize the MORE object.
//
if( More.Initialize() ) {
//
// Do the paging
//
More.DoPaging(); } } }
DEFINE_CONSTRUCTOR( MORE, PROGRAM );
BOOLEAN MORE::Initialize ( )
/*++
Routine Description:
Initializes the MORE object
Arguments:
None.
Return Value:
None.
Notes:
--*/
{ //
// Initialize program object
//
if( !PROGRAM::Initialize( MORE_MESSAGE_USAGE, MORE_ERROR_NO_MEMORY, EXIT_ERROR ) ) {
return FALSE; }
//
// Initialize whatever needs initialization
//
InitializeThings();
//
// Do the argument parsing
//
SetArguments();
return TRUE;
}
VOID MORE::Construct ( ) /*++
Routine Description:
Construct a MORE object
Arguments:
None.
Return Value:
None.
Notes:
--*/
{ _Keyboard = NULL;
_FilesArgument = NULL; _LineDelimiters = NULL; _Percent = NULL; _Line = NULL; _Help = NULL; _DisplayLinesOption = NULL; _SkipLinesOption = NULL; _NextFileOption = NULL; _ShowLineNumberOption = NULL; _QuitOption = NULL; _Help1Option = NULL; _Help2Option = NULL; }
MORE::~MORE ( )
/*++
Routine Description:
Destructs a MORE object
Arguments:
None.
Return Value:
None.
Notes:
--*/
{ //
// Deallocate the global structures previously allocated
//
DeallocateThings();
//
// Exit without error
//
exit( EXIT_NORMAL );
}
VOID MORE::InitializeThings ( )
/*++
Routine Description:
Initializes the global variables that need initialization
Arguments:
None.
Return Value:
None.
Notes:
--*/
{
if ( //
// Initialize the library
//
!(_Keyboard = NEW KEYBOARD)
) {
exit( EXIT_ERROR );
}
// MORE translates from MBCS to Unicode according to the
// current console codepage.
//
WSTRING::SetConsoleConversions();
if ( //
// Pager stuff
//
!DEFINE_CLASS_DESCRIPTOR( PAGER ) || //
// Misc. Strings
//
((_LineDelimiters = NEW DSTRING) == NULL ) || !_LineDelimiters->Initialize( "\r\n" ) || ((_Percent = NEW DSTRING) == NULL ) || ((_Line = NEW DSTRING) == NULL ) || ((_OtherPrompt = NEW DSTRING) == NULL ) ) {
Fatal();
}
//
// Get the strings containing valid user options
//
if ( (( _Help = QueryMessageString( MORE_HELP )) == NULL ) || (( _DisplayLinesOption = QueryMessageString( MORE_OPTION_DISPLAYLINES )) == NULL ) || (( _SkipLinesOption = QueryMessageString( MORE_OPTION_SKIPLINES )) == NULL ) || (( _NextFileOption = QueryMessageString( MORE_OPTION_NEXTFILE )) == NULL ) || (( _ShowLineNumberOption = QueryMessageString( MORE_OPTION_SHOWLINENUMBER )) == NULL ) || (( _QuitOption = QueryMessageString( MORE_OPTION_QUIT )) == NULL ) || (( _Help1Option = QueryMessageString( MORE_OPTION_HELP1 )) == NULL ) || (( _Help2Option = QueryMessageString( MORE_OPTION_HELP2 )) == NULL ) ) {
Fatal(); }
_Keyboard->Initialize(); _Quit = FALSE; _ExtendedModeSwitch = FALSE; _ClearScreenSwitch = FALSE; _ExpandFormFeedSwitch = FALSE; _SqueezeBlanksSwitch = FALSE; _HelpSwitch = FALSE; _StartAtLine = 0; _TabExp = DEFAULT_TABEXP; _FilesArgument = NULL;
}
VOID MORE::DeallocateThings ( )
/*++
Routine Description:
Deallocates the global variables that need deallocation
Arguments:
None.
Return Value:
None.
Notes:
--*/
{
DELETE( _Keyboard );
DELETE( _FilesArgument ); DELETE( _LineDelimiters ); DELETE( _Percent ); DELETE( _Line ); DELETE( _Help );
//
// Delete the strings containing valid user options
//
DELETE( _DisplayLinesOption ); DELETE( _SkipLinesOption ); DELETE( _NextFileOption ); DELETE( _ShowLineNumberOption ); DELETE( _QuitOption ); DELETE( _Help1Option ); DELETE( _Help2Option );
}
VOID MORE::DoPaging ( )
/*++
Routine Description:
Does the paging.
Arguments:
None.
Return Value:
None.
Notes:
--*/
{ PPATH Path; PITERATOR Iterator; BOOLEAN IsFirstFile = TRUE; ULONG FilesLeft; PFSN_FILE FsnFile; PFILE_STREAM FileStream;
FilesLeft = _FilesArgument->QueryPathCount();
if ( FilesLeft > 0 ) {
//
// We have a list of files, we will page each one in turn
//
// Get an iterator for going thru the file list
//
if ((Iterator = _FilesArgument->GetPathArray()->QueryIterator()) == NULL ) {
Fatal(); }
Path = (PPATH)Iterator->GetNext();
//
// Iterate thru all the files in the array
//
while ( Path && !_Quit) {
//
// Get a new stream out of the file name
//
if ((FsnFile = SYSTEM::QueryFile( Path )) == NULL || (FileStream = FsnFile->QueryStream( READ_ACCESS )) == NULL ) { Fatal( EXIT_ERROR, MORE_ERROR_CANNOT_ACCESS, "%W", Path->GetPathString() ); }
PageStream( FileStream, FsnFile, IsFirstFile ? _StartAtLine : 0, --FilesLeft );
DELETE( FileStream ); DELETE( FsnFile );
Path = (PPATH)Iterator->GetNext(); IsFirstFile = FALSE; }
DELETE( Iterator );
} else {
//
// The user did'nt specify a file list, so we will page
// standard input.
//
PageStream( GetStandardInput(), NULL, _StartAtLine, 0 ); } }
VOID MORE::PageStream ( IN PSTREAM Stream, IN PFSN_FILE FsnFile, IN ULONG FirstLineToDisplay, IN ULONG FilesLeft )
/*++
Routine Description:
Pages a stream
Arguments:
Stream - Supplies pointer to stream FsnFile - Supplies pointer to file object FirstLineToDisplay - Supplies first line to display FilesLeft - Files remaining to be displayed
Return Value:
None.
Notes:
--*/
{
PAGER Pager; ULONG LinesToDisplay; BOOLEAN ClearScreen; BOOLEAN StayInFile;
//
// Initialize the pager
//
if (!Pager.Initialize( Stream, this)) {
Fatal();
}
//
// Skip to the first line to be displayed
//
if ( FirstLineToDisplay > 0 ) { Pager.SkipLines( FirstLineToDisplay, _TabExp ); }
LinesToDisplay = Pager.QueryLinesPerPage() - 1; ClearScreen = _ClearScreenSwitch; StayInFile = TRUE;
while (StayInFile && Pager.ThereIsMoreToPage() && !_Quit) {
// If QueryLinesPerPage() returns 0 then undo the -1 operation.
if (LinesToDisplay == (ULONG) -1) { LinesToDisplay = 0; }
//
// Display a group of lines
//
Pager.DisplayPage( LinesToDisplay, ClearScreen, _SqueezeBlanksSwitch, _ExpandFormFeedSwitch, _TabExp );
//
// If not at end of stream, we wait for an option
//
if (Pager.ThereIsMoreToPage() || (FilesLeft > 0)) {
StayInFile = DoOption( FsnFile, &Pager, &LinesToDisplay, &ClearScreen ); } } }
BOOLEAN MORE::DoOption ( IN PFSN_FILE FsnFile, IN PPAGER Pager, OUT PULONG LinesInPage, OUT PBOOLEAN ClearScreen )
/*++
Routine Description:
Gets an option from the user
Arguments:
FsnFile - Supplies pointer to file object Pager - Supplies pointer to pager LinesInpage - Supplies pointer to lines to display in next page ClearScreen - Supplies pointer to Clearscreen flag.
Return Value:
TRUE if paging should continue for this file, FALSE otherwise
--*/
{
WCHAR Char; DSTRING String; BOOLEAN ShowLineNumber = FALSE; BOOLEAN ShowHelp = FALSE; LONG Number;
String.Initialize( " " );
while ( TRUE ) {
//
// Display prompt
//
Prompt( FsnFile, Pager, ShowLineNumber, ShowHelp, 0 );
ShowHelp = FALSE; ShowLineNumber = FALSE;
//
// Get option from the user
//
_Keyboard->DisableLineMode(); _Keyboard->ReadChar( &Char ); _Keyboard->EnableLineMode(); String.SetChAt(Char, 0); String.Strupr(); Pager->ClearLine();
//
// If Ctl-C, get out
//
if ( Char == CTRLC_CHARACTER ) {
_Keyboard->EnableLineMode(); GenerateConsoleCtrlEvent( CTRL_C_EVENT, 0 ); _Quit = TRUE; return FALSE; }
//
// If not in extended mode, any key just advances one page
//
if ( !_ExtendedModeSwitch ) { *LinesInPage = Pager->QueryLinesPerPage() - 1; return TRUE; }
//
// Now take the proper action
//
if ( String.QueryChAt(0) == (WCHAR)CARRIAGERETURN ) {
//
// Display next line of the file
//
*LinesInPage = 1; *ClearScreen = FALSE; return TRUE;
} else if ( String.QueryChAt(0) == (WCHAR)' ' ) {
//
// Display next page
//
*ClearScreen = _ClearScreenSwitch; *LinesInPage = Pager->QueryLinesPerPage() - 1; return TRUE;
} else if ( String.Stricmp(_DisplayLinesOption) == 0 ) {
//
// Display a certain number of lines. Get the number of lines
// to display
//
Prompt( FsnFile, Pager, ShowLineNumber, ShowHelp, MORE_LINEPROMPT );
*LinesInPage = ReadNumber();
//if ( ReadLine( _Keyboard, &String ) &&
// String.QueryNumber((PLONG)LinesInPage) ) {
//
// (*LinesInPage)--;
//
//} else {
// *LinesInPage = 0;
//}
Pager->ClearLine();
*ClearScreen = FALSE; return TRUE;
} else if ( String.Stricmp(_SkipLinesOption) == 0 ) {
//
// Skip a certain number of lines and then display a page.
//
Prompt( FsnFile, Pager, ShowLineNumber, ShowHelp, MORE_LINEPROMPT );
Number = ReadNumber( ); if ( Number ) { Pager->SkipLines( Number, _TabExp ); }
Pager->ClearLine();
*LinesInPage = Pager->QueryLinesPerPage() - 1; return TRUE;
} else if ( String.Stricmp(_NextFileOption) == 0 ) {
//
// Stop paging this file
//
return FALSE;
} else if ( String.Stricmp(_QuitOption) == 0 ) {
//
// Quit the program
//
_Quit = TRUE; return FALSE;
} else if ( String.Stricmp(_ShowLineNumberOption) == 0) {
//
// Prompt again, showing the line number within the file
//
ShowLineNumber = TRUE;
} else if ( ( String.Stricmp(_Help1Option) == 0) || ( String.Stricmp(_Help2Option) == 0)) {
//
// Prompt again, showing a message line
//
ShowHelp = TRUE;
}
}
}
VOID MORE::Prompt ( IN PFSN_FILE FsnFile, IN PPAGER Pager, IN BOOLEAN ShowLineNumber, IN BOOLEAN ShowHelp, IN MSGID OtherMsgId )
/*++
Routine Description:
Displays prompt. The prompt consists of a "base" prompt (e.g. "-- More --" plus various optional strings:
- Percentage of the file displayed so far. - Line number within the file - Help - Other (e.g prompt for a number )
Arguments:
FsnFile - Supplies pointer to file object Pager - Supplies pointer to pager ShowLineNumber - Supplies flag which if TRUE causes the current line numnber to be displayed HelpMsg - Supplies flag which if TRUE causes a brief help to be displayed
OtherMsg - Supplies MsgId of any other string to be displayed
Return Value:
none
--*/
{
CHAR NullBuffer = NULL_CHARACTER; PVOID PercentMsg; PVOID LineMsg; PVOID HelpMsg; PVOID OtherMsg;
//
// Obtain all the strings that form part of the prompt
//
if ( FsnFile != NULL ) { SYSTEM::QueryResourceString( _Percent, MORE_PERCENT, "%d", (Pager->QueryCurrentByte() * 100) / FsnFile->QuerySize()); _Percent->QuerySTR( 0, TO_END, (PSTR)_StringBuffer0, STRING_BUFFER_SIZE); PercentMsg = (PVOID)_StringBuffer0; } else { PercentMsg = (PVOID)&NullBuffer; }
if (ShowLineNumber) { SYSTEM::QueryResourceString( _Line, MORE_LINE, "%d", Pager->QueryCurrentLine()); _Line->QuerySTR( 0, TO_END, (PSTR)_StringBuffer1, STRING_BUFFER_SIZE); LineMsg = (PVOID)_StringBuffer1; } else { LineMsg = (PVOID)&NullBuffer; }
if (ShowHelp) { _Help->QuerySTR(0, TO_END, (PSTR)_StringBuffer2, STRING_BUFFER_SIZE); HelpMsg = (PVOID)_StringBuffer2; } else { HelpMsg = (PVOID)&NullBuffer; }
if (OtherMsgId != 0) { SYSTEM::QueryResourceString( _OtherPrompt, OtherMsgId, "" ); _OtherPrompt->QuerySTR(0, TO_END, (PSTR)_StringBuffer3, STRING_BUFFER_SIZE); OtherMsg = (PVOID)_StringBuffer3; } else { OtherMsg = (PVOID)&NullBuffer; }
//
// Now display the prompt
//
DisplayMessage( MORE_PROMPT, NORMAL_MESSAGE, "%s%s%s%s", PercentMsg, LineMsg, HelpMsg, OtherMsg ); }
PWSTRING MORE::QueryMessageString ( IN MSGID MsgId ) /*++
Routine Description:
Obtains a string object initialized to the contents of some message
Arguments:
MsgId - Supplies ID of the message
Return Value:
Pointer to initialized string object
Notes:
--*/
{
PWSTRING String;
if ( ((String = NEW DSTRING) == NULL ) || !(SYSTEM::QueryResourceString( String, MsgId, "" )) ) {
DELETE( String ); String = NULL; }
return String;
}
ULONG MORE::ReadNumber ( ) /*++
Routine Description:
Reads a number from the keyboard.
Arguments:
None
Return Value:
Number read
Notes:
--*/
{ DSTRING NumberString; DSTRING CharString; PSTREAM StandardOut; ULONG Number = 0; LONG LongNumber; WCHAR Char; BOOLEAN Done = FALSE; ULONG DigitCount = 0;
StandardOut = GetStandardOutput();
NumberString.Initialize( "" ); CharString.Initialize( " " );
while ( !Done ) {
_Keyboard->DisableLineMode(); _Keyboard->ReadChar( &Char ); _Keyboard->EnableLineMode();
switch ( Char ) {
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': CharString.SetChAt( Char, 0 ); NumberString.Strcat( &CharString ); StandardOut->WriteChar( Char ); DigitCount++; break;
case '\b': if ( DigitCount > 0 ) { NumberString.Truncate( NumberString.QueryChCount() - 1 ); StandardOut->WriteChar( Char ); StandardOut->WriteChar( ' ' ); StandardOut->WriteChar( Char ); DigitCount--; } break;
case '\r': case '\n': Done = TRUE; break;
case CTRLC_CHARACTER: _Quit = TRUE; Done = TRUE; break;
default: break;
} }
if ( NumberString.QueryChCount() > 0 ) {
if ( NumberString.QueryNumber( &LongNumber ) ) {
Number = (ULONG)LongNumber; } }
return Number; }
|