1016 lines
27 KiB
C
1016 lines
27 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1998 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
console.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module implements the interfaces that
|
||
|
provide access to the console i/o.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Wesley Witt (wesw) 21-Oct-1998
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "cmdcons.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// This code needs to be correct for DBCS. A double-byte character takes up
|
||
|
// 2 character spaces on the screen. This means that there is not a one-to-one
|
||
|
// correlation between the unicode characters we want to work with and the
|
||
|
// representation on the screen. For this reason, the code in this module
|
||
|
// does a lot of converting into the OEM charset. When represented in the
|
||
|
// OEM charset, strlen(x) is exactly the number of char spaces taken up
|
||
|
// on the screen. (The fonts used in text mode setup are all OEM charset fonts
|
||
|
// so that's why we use OEM).
|
||
|
//
|
||
|
|
||
|
|
||
|
#define CURSOR SplangGetCursorChar()
|
||
|
#define CONSOLE_HEADER_HEIGHT 0
|
||
|
|
||
|
unsigned ConsoleX,ConsoleY;
|
||
|
|
||
|
UCHAR ConsoleLeadByteTable[128/8];
|
||
|
BOOLEAN ConsoleDbcs;
|
||
|
|
||
|
#define IS_LEAD_BYTE(c) ((c < 0x80) ? FALSE : (ConsoleLeadByteTable[(c & 0x7f) / 8] & (1 << ((c & 0x7f) % 8))))
|
||
|
#define SET_LEAD_BYTE(c) (ConsoleLeadByteTable[(c & 0x7f)/8] |= (1 << ((c & 0x7f) % 8)))
|
||
|
|
||
|
PUCHAR ConsoleOemString;
|
||
|
ULONG ConsoleMaxOemStringLen;
|
||
|
|
||
|
#define CONSOLE_ATTRIBUTE (ATT_FG_WHITE | ATT_BG_BLACK)
|
||
|
#define CONSOLE_BACKGROUND ATT_BLACK
|
||
|
|
||
|
//
|
||
|
// Globals used for more mode.
|
||
|
//
|
||
|
BOOLEAN pMoreMode;
|
||
|
unsigned pMoreLinesOut;
|
||
|
unsigned pMoreMaxLines;
|
||
|
|
||
|
#define CONSOLE_MORE_ATTRIBUTE (ATT_FG_BLACK | ATT_BG_WHITE)
|
||
|
#define CONSOLE_MORE_BACKGROUND ATT_WHITE
|
||
|
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
pRcLineDown(
|
||
|
BOOLEAN *pbScrolled
|
||
|
);
|
||
|
|
||
|
|
||
|
VOID
|
||
|
RcConsoleInit(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
unsigned i;
|
||
|
|
||
|
//
|
||
|
// Build lead-byte table, set ConsoleDbcs global
|
||
|
//
|
||
|
RtlZeroMemory(ConsoleLeadByteTable,sizeof(ConsoleLeadByteTable));
|
||
|
|
||
|
if(ConsoleDbcs = NLS_MB_OEM_CODE_PAGE_TAG) {
|
||
|
|
||
|
for(i=128; i<=255; i++) {
|
||
|
|
||
|
if((NLS_OEM_LEAD_BYTE_INFO)[i]) {
|
||
|
|
||
|
SET_LEAD_BYTE(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get a buffer for unicode to oem translations.
|
||
|
//
|
||
|
ConsoleMaxOemStringLen = 2000;
|
||
|
ConsoleOemString = SpMemAlloc(ConsoleMaxOemStringLen);
|
||
|
|
||
|
//
|
||
|
// Clear screen and initialize cursor location.
|
||
|
//
|
||
|
pRcCls();
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
RcConsoleTerminate(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
ASSERT(ConsoleOemString);
|
||
|
|
||
|
SpMemFree(ConsoleOemString);
|
||
|
ConsoleOemString = NULL;
|
||
|
ConsoleMaxOemStringLen = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define MAX_HISTORY_LINES 30
|
||
|
|
||
|
typedef struct _LINE_HISTORY {
|
||
|
WCHAR Line[RC_MAX_LINE_LEN];
|
||
|
ULONG Length;
|
||
|
} LINE_HISTORY, *PLINE_HISTORY;
|
||
|
|
||
|
LINE_HISTORY LineHistory[MAX_HISTORY_LINES];
|
||
|
ULONG CurPos;
|
||
|
ULONG NextPos;
|
||
|
|
||
|
|
||
|
void
|
||
|
RcPurgeHistoryBuffer(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
CurPos = 0;
|
||
|
NextPos = 0;
|
||
|
ZeroMemory( LineHistory, sizeof(LineHistory) );
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
RcClearToEOL(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
unsigned uWidth = _CmdConsBlock->VideoVars->ScreenWidth;
|
||
|
unsigned uY = ConsoleY + (ConsoleX / uWidth);
|
||
|
unsigned uX = ConsoleX % uWidth; // taking care of roll over
|
||
|
|
||
|
SpvidClearScreenRegion(uX, uY, uWidth-uX,
|
||
|
1, CONSOLE_BACKGROUND);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
RcClearLines(
|
||
|
unsigned uX, unsigned uY, unsigned cLines
|
||
|
)
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
|
||
|
This routine clears the specified number of lines with blank characters
|
||
|
starting from X coordinate (0 based) on lines specifed by Y coordinate
|
||
|
|
||
|
Arguments:
|
||
|
uX - starting X coordinate
|
||
|
uY - starting Y coordinate
|
||
|
cLines - number of lines to be cleared after Y coordinate
|
||
|
|
||
|
Return Value: None
|
||
|
--*/
|
||
|
{
|
||
|
unsigned uWidth = _CmdConsBlock->VideoVars->ScreenWidth;
|
||
|
|
||
|
if (uY < _CmdConsBlock->VideoVars->ScreenWidth) {
|
||
|
SpvidClearScreenRegion(uX, uY, uWidth-uX,
|
||
|
1, CONSOLE_BACKGROUND);
|
||
|
|
||
|
if (cLines && (cLines <= _CmdConsBlock->VideoVars->ScreenWidth)) {
|
||
|
SpvidClearScreenRegion(0, ++uY, uWidth,
|
||
|
cLines, CONSOLE_BACKGROUND);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
unsigned
|
||
|
_RcLineIn(
|
||
|
OUT PWCHAR Buffer,
|
||
|
IN unsigned MaxLineLen,
|
||
|
IN BOOLEAN PasswordProtect,
|
||
|
IN BOOLEAN UseBuffer
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Get a line of input from the user. The user can type at the keyboard.
|
||
|
Control is very simple, the only control character accepted is backspace.
|
||
|
A cursor will be drawn as the user types to indicate where the next
|
||
|
character will end up on the screen. As the user types the screen will be
|
||
|
scrolled if necessary.
|
||
|
|
||
|
The string returned will be limited to MaxLineLen-1 characters and
|
||
|
0-terminated.
|
||
|
|
||
|
NOTE: this routine deals with double-byte characters correctly.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Buffer - receives the line as typed by the user. The buffer must be
|
||
|
large enough to hold least 2 characters, since the string returned
|
||
|
will always get a nul-termination and requesting a string that
|
||
|
can have at most only a terminating nul isn't too meaningful.
|
||
|
|
||
|
MaxLineLen - supplies the number of unicode characters that will fit
|
||
|
in the buffer pointed to by Buffer (including the terminating nul).
|
||
|
As described above, must be > 1.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Number of characters written into Buffer, not including the terminating
|
||
|
nul character.
|
||
|
|
||
|
Upon return the global ConsoleX and ConsoleY variables will be updated
|
||
|
such that ConsoleX is 0 and ConsoleY indicates the next "empty" line.
|
||
|
Also the cursor will be shut off.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
unsigned LineLen;
|
||
|
ULONG c;
|
||
|
WCHAR s[2];
|
||
|
UCHAR Oem[3];
|
||
|
BOOL Done;
|
||
|
ULONG OemLen;
|
||
|
int i,j;
|
||
|
ULONG OrigConsoleX;
|
||
|
ULONG ulOrigY;
|
||
|
BOOLEAN bScrolled = FALSE;
|
||
|
|
||
|
ASSERT(MaxLineLen > 1);
|
||
|
MaxLineLen--; // leave room for terminating nul
|
||
|
LineLen = 0;
|
||
|
Done = FALSE;
|
||
|
s[1] = 0;
|
||
|
|
||
|
//
|
||
|
// We use ConsoleOemString as temp storage for char lengths.
|
||
|
// Make sure we don't run off the end of the buffer.
|
||
|
//
|
||
|
if(MaxLineLen > ConsoleMaxOemStringLen) {
|
||
|
MaxLineLen = ConsoleMaxOemStringLen;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Turn cursor on.
|
||
|
//
|
||
|
s[0] = CURSOR;
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
|
||
|
//
|
||
|
// Get characters until user hits enter.
|
||
|
//
|
||
|
CurPos = NextPos;
|
||
|
OrigConsoleX = ConsoleX;
|
||
|
ulOrigY = ConsoleY;
|
||
|
|
||
|
if (UseBuffer) {
|
||
|
LineLen = wcslen(Buffer);
|
||
|
RtlZeroMemory(ConsoleOemString,ConsoleMaxOemStringLen);
|
||
|
RtlUnicodeToOemN(ConsoleOemString,ConsoleMaxOemStringLen,&OemLen,Buffer,LineLen*sizeof(WCHAR));
|
||
|
SpvidDisplayOemString(ConsoleOemString,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
ConsoleX += OemLen;
|
||
|
RcClearToEOL();
|
||
|
s[0] = CURSOR;
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
for (i=0; i<(int)wcslen(Buffer); i++) {
|
||
|
RtlUnicodeToOemN(Oem,3,&OemLen,&Buffer[i],2*sizeof(WCHAR));
|
||
|
ConsoleOemString[i] = (UCHAR)OemLen-1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
|
||
|
c = SpInputGetKeypress();
|
||
|
|
||
|
if(c & KEY_NON_CHARACTER) {
|
||
|
if (c == KEY_UP || c == KEY_DOWN) {
|
||
|
if (c == KEY_UP) {
|
||
|
if (CurPos == 0) {
|
||
|
i = MAX_HISTORY_LINES - 1;
|
||
|
} else {
|
||
|
i = CurPos - 1;
|
||
|
}
|
||
|
j = i;
|
||
|
while (LineHistory[i].Length == 0) {
|
||
|
i -= 1;
|
||
|
if (i < 0) {
|
||
|
i = MAX_HISTORY_LINES - 1;
|
||
|
}
|
||
|
if (i == j) break;
|
||
|
}
|
||
|
}
|
||
|
if (c == KEY_DOWN) {
|
||
|
if (CurPos == MAX_HISTORY_LINES) {
|
||
|
i = 0;
|
||
|
} else {
|
||
|
i = CurPos + 1;
|
||
|
}
|
||
|
j = i;
|
||
|
while (LineHistory[i].Length == 0) {
|
||
|
i += 1;
|
||
|
if (i == MAX_HISTORY_LINES) {
|
||
|
i = 0;
|
||
|
}
|
||
|
if (i == j) break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (LineHistory[i].Length) {
|
||
|
wcscpy(Buffer,LineHistory[i].Line);
|
||
|
LineLen = LineHistory[i].Length;
|
||
|
RtlZeroMemory(ConsoleOemString,ConsoleMaxOemStringLen);
|
||
|
RtlUnicodeToOemN(ConsoleOemString,ConsoleMaxOemStringLen,&OemLen,Buffer,LineLen*sizeof(WCHAR));
|
||
|
ConsoleX = OrigConsoleX;
|
||
|
|
||
|
// clear the old command
|
||
|
RcClearLines(ConsoleX, ulOrigY, ConsoleY - ulOrigY);
|
||
|
|
||
|
ConsoleY = ulOrigY;
|
||
|
|
||
|
// scroll if needed
|
||
|
if ((ConsoleX + OemLen) >= _CmdConsBlock->VideoVars->ScreenWidth) {
|
||
|
int cNumLines = (ConsoleX + OemLen) /
|
||
|
_CmdConsBlock->VideoVars->ScreenWidth;
|
||
|
int cAvailLines = _CmdConsBlock->VideoVars->ScreenHeight -
|
||
|
ConsoleY - 1;
|
||
|
|
||
|
if (cNumLines > cAvailLines) {
|
||
|
cNumLines -= cAvailLines;
|
||
|
|
||
|
SpvidScrollUp( CONSOLE_HEADER_HEIGHT,
|
||
|
_CmdConsBlock->VideoVars->ScreenHeight - 1,
|
||
|
cNumLines,
|
||
|
CONSOLE_BACKGROUND
|
||
|
);
|
||
|
|
||
|
ConsoleY -= cNumLines;
|
||
|
ulOrigY = ConsoleY;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SpvidDisplayOemString(ConsoleOemString,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
|
||
|
// clean up trailing spaces left by previous command
|
||
|
ConsoleX += OemLen;
|
||
|
//RcClearToEOL();
|
||
|
s[0] = CURSOR;
|
||
|
|
||
|
if (ConsoleX >= _CmdConsBlock->VideoVars->ScreenWidth) {
|
||
|
ConsoleY += (ConsoleX / _CmdConsBlock->VideoVars->ScreenWidth);
|
||
|
ConsoleY %= _CmdConsBlock->VideoVars->ScreenHeight;
|
||
|
ConsoleX %= _CmdConsBlock->VideoVars->ScreenWidth;
|
||
|
}
|
||
|
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE, ConsoleX, ConsoleY);
|
||
|
|
||
|
CurPos = i;
|
||
|
|
||
|
for (i=0; i<(int)wcslen(Buffer); i++) {
|
||
|
RtlUnicodeToOemN(Oem,3,&OemLen,&Buffer[i],2*sizeof(WCHAR));
|
||
|
ConsoleOemString[i] = (UCHAR)OemLen-1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// Got a real unicode value, which could be CR, etc.
|
||
|
//
|
||
|
s[0] = (WCHAR)c;
|
||
|
|
||
|
switch(s[0]) {
|
||
|
|
||
|
case ASCI_ESC:
|
||
|
LineLen = 0;
|
||
|
ConsoleX = OrigConsoleX;
|
||
|
CurPos = NextPos;
|
||
|
// clear the extra lines from previous command if any
|
||
|
RcClearLines(ConsoleX, ulOrigY, ConsoleY - ulOrigY);
|
||
|
//RcClearToEOL();
|
||
|
s[0] = CURSOR;
|
||
|
ConsoleY = ulOrigY;
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case ASCI_BS:
|
||
|
|
||
|
if(LineLen) {
|
||
|
LineLen--;
|
||
|
|
||
|
//
|
||
|
// Write a space over the current cursor location
|
||
|
// and then back up one char and write the cursor.
|
||
|
//
|
||
|
s[0] = L' ';
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
|
||
|
OemLen = ConsoleOemString[LineLen];
|
||
|
ASSERT(OemLen <= 2);
|
||
|
|
||
|
if(ConsoleX) {
|
||
|
//
|
||
|
// We might have the case where the character we just erased
|
||
|
// is a double-byte character that didn't fit on the previous
|
||
|
// line because the user typed it when the cursor was at the
|
||
|
// rightmost x position.
|
||
|
//
|
||
|
if(OemLen) {
|
||
|
//
|
||
|
// No special case needed. Decrement the x position and
|
||
|
// clear out the second half of double-byte char,
|
||
|
// if necessary.
|
||
|
//
|
||
|
ConsoleX -= OemLen;
|
||
|
if(OemLen == 2) {
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX+1,ConsoleY);
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// Clear out the current character (which must be
|
||
|
// a double-byte character) and then hop up one line.
|
||
|
//
|
||
|
ASSERT(ConsoleX == 2);
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,0,ConsoleY);
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,1,ConsoleY);
|
||
|
|
||
|
ConsoleX = _CmdConsBlock->VideoVars->ScreenWidth-1;
|
||
|
ConsoleY--;
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// The cursor is at x=0. This can't happen if
|
||
|
// there's a fill space at the end of the previous line,
|
||
|
// so we don't need to worry about handling that here.
|
||
|
//
|
||
|
ASSERT(OemLen != 3);
|
||
|
ConsoleX = _CmdConsBlock->VideoVars->ScreenWidth - OemLen;
|
||
|
ConsoleY--;
|
||
|
|
||
|
//
|
||
|
// Clear out second half of double-byte char if necessary.
|
||
|
//
|
||
|
if(OemLen > 1) {
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX+1,ConsoleY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
s[0] = CURSOR;
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
}
|
||
|
|
||
|
ulOrigY = ConsoleY;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case ASCI_CR:
|
||
|
//
|
||
|
// Erase the cursor and advance the current position one line.
|
||
|
//
|
||
|
s[0] = L' ';
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
|
||
|
ConsoleX = 0;
|
||
|
pRcLineDown(0);
|
||
|
|
||
|
//
|
||
|
// We know there's room in the buffer because we accounted
|
||
|
// for the terminating nul up front.
|
||
|
//
|
||
|
Buffer[LineLen] = 0;
|
||
|
Done = TRUE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
//
|
||
|
// Plain old character. Note that it could be a double-byte char,
|
||
|
// which takes up 2 char widths on the screen.
|
||
|
//
|
||
|
// Also disallow control chars.
|
||
|
//
|
||
|
if((s[0] >= L' ') && (LineLen < MaxLineLen)) {
|
||
|
|
||
|
//
|
||
|
// Convert to OEM, including nul byte
|
||
|
//
|
||
|
RtlUnicodeToOemN(Oem,3,&OemLen,PasswordProtect?L"*":s,2*sizeof(WCHAR));
|
||
|
OemLen--;
|
||
|
|
||
|
//
|
||
|
// Save the character in the caller's buffer.
|
||
|
// Also we use the ConsoleOemString buffer as temp storage space
|
||
|
// to store the length in oem chars of each char input.
|
||
|
//
|
||
|
Buffer[LineLen] = s[0];
|
||
|
ConsoleOemString[LineLen] = (UCHAR)OemLen;
|
||
|
|
||
|
//
|
||
|
// If the character is double-byte, then there might not be
|
||
|
// enough room on the current line for it. Check for that here.
|
||
|
//
|
||
|
if((ConsoleX+OemLen) > _CmdConsBlock->VideoVars->ScreenWidth) {
|
||
|
|
||
|
//
|
||
|
// Erase the cursor from the last position on the line.
|
||
|
//
|
||
|
s[0] = L' ';
|
||
|
SpvidDisplayString(
|
||
|
s,
|
||
|
CONSOLE_ATTRIBUTE,
|
||
|
_CmdConsBlock->VideoVars->ScreenWidth-1,
|
||
|
ConsoleY
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Adjust cursor position.
|
||
|
//
|
||
|
ConsoleX = 0;
|
||
|
// >> ulOrigY = ConsoleY;
|
||
|
bScrolled = FALSE;
|
||
|
pRcLineDown(&bScrolled);
|
||
|
|
||
|
//
|
||
|
// if screen scrolled then we need to adjust original Y coordinate
|
||
|
// appropriately
|
||
|
//
|
||
|
if (bScrolled && (ulOrigY > 0))
|
||
|
--ulOrigY;
|
||
|
|
||
|
//
|
||
|
// Special handling for this case, so backspace will
|
||
|
// work correctly.
|
||
|
//
|
||
|
ConsoleOemString[LineLen] = 0;
|
||
|
}
|
||
|
|
||
|
SpvidDisplayOemString(Oem,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
ConsoleX += OemLen;
|
||
|
|
||
|
if(ConsoleX == _CmdConsBlock->VideoVars->ScreenWidth) {
|
||
|
ConsoleX = 0;
|
||
|
// >> ulOrigY = ConsoleY;
|
||
|
bScrolled = FALSE;
|
||
|
pRcLineDown(&bScrolled);
|
||
|
|
||
|
//
|
||
|
// if screen scrolled then we need to adjust original Y coordinate
|
||
|
// appropriately
|
||
|
//
|
||
|
if (bScrolled && (ulOrigY > 0))
|
||
|
--ulOrigY;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now display cursor at cursor position for next character.
|
||
|
//
|
||
|
s[0] = CURSOR;
|
||
|
SpvidDisplayString(s,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
|
||
|
LineLen++;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} while(!Done);
|
||
|
|
||
|
Buffer[LineLen] = 0;
|
||
|
|
||
|
// save the line for future use only if its not a password
|
||
|
if (LineLen && !PasswordProtect) {
|
||
|
LineHistory[NextPos].Length = LineLen;
|
||
|
wcscpy(LineHistory[NextPos].Line,Buffer);
|
||
|
|
||
|
NextPos += 1;
|
||
|
if (NextPos >= MAX_HISTORY_LINES) {
|
||
|
NextPos = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return LineLen;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
RcRawTextOut(
|
||
|
IN LPCWSTR Text,
|
||
|
IN LONG Length
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Write a text string to the console at the current position
|
||
|
(incidated by the ConsoleX and ConsoleY global variables). If the string
|
||
|
is longer than will fit on the current line, it is broken up properly
|
||
|
to span multiple lines. The screen is scrolled if necessary.
|
||
|
|
||
|
This routine handles double-byte characters correctly, ensuring that
|
||
|
no double-byte character is split across lines.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Text - supplies the text to be output. Text strings longer than
|
||
|
ConsoleMaxOemStringLen are truncated. The string need not be
|
||
|
nul-terminated if Length is not -1.
|
||
|
|
||
|
Length - supplies the number of characters to output. If -1, then
|
||
|
Text is assumed to be nul-terminated and the length is
|
||
|
calculated automatically.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
FALSE if we're in more mode and when prompted the user hit esc.
|
||
|
TRUE otherwise.
|
||
|
|
||
|
Upon return, the global variables ConsoleX and ConsoleY point at the next
|
||
|
empty location on the screen.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ULONG OemLen;
|
||
|
ULONG len;
|
||
|
ULONG i;
|
||
|
UCHAR c;
|
||
|
PUCHAR p;
|
||
|
PUCHAR LastLead;
|
||
|
BOOLEAN NewLine;
|
||
|
BOOLEAN Dbcs;
|
||
|
|
||
|
//
|
||
|
// Convert the string to the OEM charset and determine the number
|
||
|
// of character spaces the string will occupy on-screen, which is
|
||
|
// equal to the number of bytes in the OEM representation of the string.
|
||
|
// If this is not the same as the number of Unicode characters
|
||
|
// in the string, then we've got a string with double-byte chars in it.
|
||
|
//
|
||
|
len = ((Length == -1) ? wcslen(Text) : Length);
|
||
|
|
||
|
RtlUnicodeToOemN(
|
||
|
ConsoleOemString,
|
||
|
ConsoleMaxOemStringLen,
|
||
|
&OemLen,
|
||
|
(PVOID)Text,
|
||
|
len * sizeof(WCHAR)
|
||
|
);
|
||
|
|
||
|
Dbcs = (OemLen != len);
|
||
|
|
||
|
//
|
||
|
// If we think we have a double-byte string, we better be prepared
|
||
|
// to handle it properly.
|
||
|
//
|
||
|
if(Dbcs) {
|
||
|
ASSERT(NLS_MB_OEM_CODE_PAGE_TAG);
|
||
|
ASSERT(ConsoleDbcs);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Spit out the prompt in pieces until we've got all the characters
|
||
|
// displayed.
|
||
|
//
|
||
|
ASSERT(ConsoleX < _CmdConsBlock->VideoVars->ScreenWidth);
|
||
|
ASSERT(ConsoleY < _CmdConsBlock->VideoVars->ScreenHeight);
|
||
|
p = ConsoleOemString;
|
||
|
|
||
|
while(OemLen) {
|
||
|
|
||
|
if((ConsoleX+OemLen) > _CmdConsBlock->VideoVars->ScreenWidth) {
|
||
|
|
||
|
len = _CmdConsBlock->VideoVars->ScreenWidth - ConsoleX;
|
||
|
|
||
|
//
|
||
|
// Avoid splitting a double-byte character across lines.
|
||
|
//
|
||
|
if(Dbcs) {
|
||
|
for(LastLead=NULL,i=0; i<len; i++) {
|
||
|
if(IS_LEAD_BYTE(p[i])) {
|
||
|
LastLead = &p[i];
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
if(LastLead == &p[len-1]) {
|
||
|
len--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NewLine = TRUE;
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// It fits on the current line, just display it.
|
||
|
//
|
||
|
len = OemLen;
|
||
|
NewLine = ((ConsoleX+len) == _CmdConsBlock->VideoVars->ScreenWidth);
|
||
|
}
|
||
|
|
||
|
c = p[len];
|
||
|
p[len] = 0;
|
||
|
SpvidDisplayOemString(p,CONSOLE_ATTRIBUTE,ConsoleX,ConsoleY);
|
||
|
p[len] = c;
|
||
|
|
||
|
p += len;
|
||
|
OemLen -= len;
|
||
|
|
||
|
if(NewLine) {
|
||
|
ConsoleX = 0;
|
||
|
if(!pRcLineDown(0)) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
} else {
|
||
|
ConsoleX += len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
RcBatchOut(
|
||
|
IN PWSTR strW
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
ULONG OemLen;
|
||
|
ULONG len;
|
||
|
|
||
|
|
||
|
len = wcslen(strW);
|
||
|
|
||
|
RtlUnicodeToOemN(
|
||
|
ConsoleOemString,
|
||
|
ConsoleMaxOemStringLen,
|
||
|
&len,
|
||
|
(PVOID)strW,
|
||
|
len * sizeof(WCHAR)
|
||
|
);
|
||
|
|
||
|
Status = ZwWriteFile(
|
||
|
OutputFileHandle,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&IoStatusBlock,
|
||
|
(PVOID)ConsoleOemString,
|
||
|
len,
|
||
|
&OutputFileOffset,
|
||
|
NULL
|
||
|
);
|
||
|
if (NT_SUCCESS(Status)) {
|
||
|
OutputFileOffset.LowPart += len;
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
RcTextOut(
|
||
|
IN LPCWSTR Text
|
||
|
)
|
||
|
{
|
||
|
LPCWSTR p,q;
|
||
|
|
||
|
|
||
|
if (InBatchMode && OutputFileHandle) {
|
||
|
if (RcBatchOut( (LPWSTR)Text ) == STATUS_SUCCESS) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
if (InBatchMode && RedirectToNULL) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
p = Text;
|
||
|
while(*p) {
|
||
|
//
|
||
|
// Locate line terminator, which is cr, lf, or nul.
|
||
|
//
|
||
|
q = p;
|
||
|
while(*q && (*q != L'\r') && (*q != L'\n')) {
|
||
|
q++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Print this segment out.
|
||
|
//
|
||
|
if(!RcRawTextOut(p,(LONG)(q-p))) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Handle cr's and lf's.
|
||
|
//
|
||
|
p = q;
|
||
|
|
||
|
while((*p == L'\n') || (*p == L'\r')) {
|
||
|
|
||
|
if(*p == L'\n') {
|
||
|
if(!pRcLineDown(0)) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
} else {
|
||
|
if(*p == L'\r') {
|
||
|
ConsoleX = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
pRcLineDown(
|
||
|
BOOLEAN *pbScrolled
|
||
|
)
|
||
|
{
|
||
|
WCHAR *p;
|
||
|
unsigned u;
|
||
|
ULONG c;
|
||
|
BOOLEAN b;
|
||
|
|
||
|
if (pbScrolled)
|
||
|
*pbScrolled = FALSE;
|
||
|
|
||
|
b = TRUE;
|
||
|
|
||
|
ConsoleY++;
|
||
|
pMoreLinesOut++;
|
||
|
|
||
|
if(ConsoleY == _CmdConsBlock->VideoVars->ScreenHeight) {
|
||
|
//
|
||
|
// Reached the bottom of the screen, need to scroll.
|
||
|
//
|
||
|
ConsoleY--;
|
||
|
|
||
|
SpvidScrollUp(
|
||
|
CONSOLE_HEADER_HEIGHT,
|
||
|
_CmdConsBlock->VideoVars->ScreenHeight-1,
|
||
|
1,
|
||
|
CONSOLE_BACKGROUND
|
||
|
);
|
||
|
|
||
|
if (pbScrolled)
|
||
|
*pbScrolled = TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we're in more mode and we've output the max number of lines
|
||
|
// allowed before requiring user input, get that input now.
|
||
|
//
|
||
|
if(pMoreMode
|
||
|
&& (pMoreLinesOut == pMoreMaxLines)
|
||
|
&& (p = SpRetreiveMessageText(ImageBase,MSG_MORE_PROMPT,NULL,0))) {
|
||
|
|
||
|
//
|
||
|
// Don't bother calling the format message routine, since that
|
||
|
// requires some other buffer. Just strip off cr/lf manually.
|
||
|
//
|
||
|
u = wcslen(p);
|
||
|
while(u && ((p[u-1] == L'\r') || (p[u-1] == L'\n'))) {
|
||
|
p[--u] = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Display the more prompt at the bottom of the screen.
|
||
|
//
|
||
|
SpvidClearScreenRegion(
|
||
|
0,
|
||
|
_CmdConsBlock->VideoVars->ScreenHeight - 1,
|
||
|
_CmdConsBlock->VideoVars->ScreenWidth,
|
||
|
1,
|
||
|
CONSOLE_MORE_BACKGROUND
|
||
|
);
|
||
|
|
||
|
SpvidDisplayString(
|
||
|
p,
|
||
|
CONSOLE_MORE_ATTRIBUTE,
|
||
|
2,
|
||
|
_CmdConsBlock->VideoVars->ScreenHeight - 1
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// We don't need the prompt any more.
|
||
|
//
|
||
|
SpMemFree(p);
|
||
|
|
||
|
//
|
||
|
// Wait for the user to hit space, cr, or esc.
|
||
|
//
|
||
|
pMoreLinesOut = 0;
|
||
|
while(1) {
|
||
|
c = SpInputGetKeypress();
|
||
|
if(c == ASCI_CR) {
|
||
|
//
|
||
|
// Allow one more line before prompting user.
|
||
|
//
|
||
|
pMoreMaxLines = 1;
|
||
|
break;
|
||
|
} else {
|
||
|
if(c == ASCI_ESC) {
|
||
|
//
|
||
|
// User wants to stop the current command.
|
||
|
//
|
||
|
b = FALSE;
|
||
|
break;
|
||
|
} else {
|
||
|
if(c == L' ') {
|
||
|
//
|
||
|
// Allow a whole page more.
|
||
|
//
|
||
|
pMoreMaxLines = _CmdConsBlock->VideoVars->ScreenHeight
|
||
|
- (CONSOLE_HEADER_HEIGHT + 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SpvidClearScreenRegion(
|
||
|
0,
|
||
|
_CmdConsBlock->VideoVars->ScreenHeight - 1,
|
||
|
_CmdConsBlock->VideoVars->ScreenWidth,
|
||
|
1,
|
||
|
CONSOLE_BACKGROUND
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return(b);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pRcEnableMoreMode(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
pMoreMode = TRUE;
|
||
|
|
||
|
pMoreLinesOut = 0;
|
||
|
|
||
|
//
|
||
|
// The maximum number of lines we allow before prompting the user
|
||
|
// is the screen height minus the header area. We also reserve
|
||
|
// one line for the prompt area.
|
||
|
//
|
||
|
pMoreMaxLines = _CmdConsBlock->VideoVars->ScreenHeight - (CONSOLE_HEADER_HEIGHT + 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pRcDisableMoreMode(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
pMoreMode = FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
RcCmdCls(
|
||
|
IN PTOKENIZED_LINE TokenizedLine
|
||
|
)
|
||
|
{
|
||
|
if (RcCmdParseHelp( TokenizedLine, MSG_CLS_HELP )) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Call worker routine to actually do the work
|
||
|
//
|
||
|
pRcCls();
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pRcCls(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Initialize location and clear screen.
|
||
|
//
|
||
|
ConsoleX = 0;
|
||
|
ConsoleY = CONSOLE_HEADER_HEIGHT;
|
||
|
|
||
|
SpvidClearScreenRegion(0,0,0,0,CONSOLE_BACKGROUND);
|
||
|
}
|