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.
 
 
 
 
 
 

911 lines
21 KiB

/*++
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;
}