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.
 
 
 
 
 
 

2522 lines
62 KiB

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
vtutf8chan.c
Abstract:
Routines for managing channels in the sac.
Author:
Sean Selitrennikoff (v-seans) Sept, 2000.
Brian Guarraci (briangu) March, 2001.
Revision History:
--*/
#include "sac.h"
//
// Macro to validate the VTUTF8 Screen matrix coordinates
//
#define ASSERT_CHANNEL_ROW_COL(_Channel) \
ASSERT(_Channel->CursorRow >= 0); \
ASSERT(_Channel->CursorRow < SAC_VTUTF8_ROW_HEIGHT); \
ASSERT(_Channel->CursorCol >= 0); \
ASSERT(_Channel->CursorCol < SAC_VTUTF8_COL_WIDTH);
//
// VTUTF8 Attribute flags
//
// Note: We use bit flags in the UCHAR
// that containts the attributes.
// Hence, there can be up to 8 attributes.
//
//#define VTUTF8_ATTRIBUTES_OFF 0x1
#define VTUTF8_ATTRIBUTE_BLINK 0x1
#define VTUTF8_ATTRIBUTE_BOLD 0x2
#define VTUTF8_ATTRIBUTE_INVERSE 0x4
//
// Internal VTUTF8 emulator command codes
//
typedef enum _SAC_ESCAPE_CODE {
CursorUp,
CursorDown,
CursorRight,
CursorLeft,
AttributesOff,
BlinkOn,
BlinkOff,
BoldOn,
BoldOff,
InverseOn,
InverseOff,
BackTab,
ClearToEol,
ClearToBol,
ClearLine,
ClearToEos,
ClearToBos,
ClearScreen,
SetCursorPosition,
SetScrollRegion,
SetColor,
SetBackgroundColor,
SetForegroundColor,
SetColorAndAttribute
} SAC_ESCAPE_CODE, *PSAC_ESCAPE_CODE;
//
// Structure for assembling well-defined
// command sequences.
//
typedef struct _SAC_STATIC_ESCAPE_STRING {
WCHAR String[10];
ULONG StringLength;
SAC_ESCAPE_CODE Code;
} SAC_STATIC_ESCAPE_STRING, *PSAC_STATIC_ESCAPE_STRING;
//
// The well-defined escape sequences.
//
// Note: add <esc>[YYYm sequences below (in consume escape sequences)
// rather than here
// Note: try to keep this list small since it gets iterated through
// for every escape sequence consumed.
// Note: it would be interesting to order these by hit frequency
//
SAC_STATIC_ESCAPE_STRING SacStaticEscapeStrings[] = {
{L"[A", sizeof(L"[A")/sizeof(WCHAR)-1, CursorUp},
{L"[B", sizeof(L"[B")/sizeof(WCHAR)-1, CursorDown},
{L"[C", sizeof(L"[C")/sizeof(WCHAR)-1, CursorRight},
{L"[D", sizeof(L"[D")/sizeof(WCHAR)-1, CursorLeft},
{L"[0Z", sizeof(L"[0Z")/sizeof(WCHAR)-1, BackTab},
{L"[K", sizeof(L"[K")/sizeof(WCHAR)-1, ClearToEol},
{L"[1K", sizeof(L"[1K")/sizeof(WCHAR)-1, ClearToBol},
{L"[2K", sizeof(L"[2K")/sizeof(WCHAR)-1, ClearLine},
{L"[J", sizeof(L"[J")/sizeof(WCHAR)-1, ClearToEos},
{L"[1J", sizeof(L"[1J")/sizeof(WCHAR)-1, ClearToBos},
{L"[2J", sizeof(L"[2J")/sizeof(WCHAR)-1, ClearScreen}
};
//
// Global defines for a default vtutf8 terminal. May be used by clients to size the
// local monitor to match the headless monitor.
//
#define ANSI_TERM_DEFAULT_ATTRIBUTES 0
#define ANSI_TERM_DEFAULT_BKGD_COLOR 40
#define ANSI_TERM_DEFAULT_TEXT_COLOR 37
//
// Enumerated ANSI escape sequences
//
typedef enum _ANSI_CMD {
ANSICmdClearDisplay,
ANSICmdClearToEndOfDisplay,
ANSICmdClearToEndOfLine,
ANSICmdSetColor,
ANSICmdPositionCursor,
ANSICmdDisplayAttributesOff,
ANSICmdDisplayInverseVideoOn,
ANSICmdDisplayInverseVideoOff,
ANSICmdDisplayBlinkOn,
ANSICmdDisplayBlinkOff,
ANSICmdDisplayBoldOn,
ANSICmdDisplayBoldOff
} ANSI_CMD, *PANSI_CMD;
//
// HeadlessCmdSetColor:
// Input structure: FgColor, BkgColor: Both colors set according to ANSI terminal
// definitons.
//
typedef struct _ANSI_CMD_SET_COLOR {
ULONG FgColor;
ULONG BkgColor;
} ANSI_CMD_SET_COLOR, *PANSI_CMD_SET_COLOR;
//
// ANSICmdPositionCursor:
// Input structure: Row, Column: Both values are zero base, with upper left being (1, 1).
//
typedef struct _ANSI_CMD_POSITION_CURSOR {
ULONG X;
ULONG Y;
} ANSI_CMD_POSITION_CURSOR, *PANSI_CMD_POSITION_CURSOR;
NTSTATUS
VTUTF8ChannelProcessAttributes(
IN PSAC_CHANNEL Channel,
IN UCHAR Attributes
);
NTSTATUS
VTUTF8ChannelAnsiDispatch(
IN PSAC_CHANNEL Channel,
IN ANSI_CMD Command,
IN PVOID InputBuffer OPTIONAL,
IN SIZE_T InputBufferSize OPTIONAL
);
VOID
VTUTF8ChannelSetIBufferIndex(
IN PSAC_CHANNEL Channel,
IN ULONG IBufferIndex
);
ULONG
VTUTF8ChannelGetIBufferIndex(
IN PSAC_CHANNEL Channel
);
NTSTATUS
VTUTF8ChannelOInit(
PSAC_CHANNEL Channel
)
/*++
Routine Description:
Initialize the Output buffer
Arguments:
Channel - the channel to initialize
Return Value:
Status
--*/
{
ULONG R;
ULONG C;
PSAC_SCREEN_BUFFER ScreenBuffer;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
//
// initialize the screen buffer
//
Channel->CurrentAttr = ANSI_TERM_DEFAULT_ATTRIBUTES;
Channel->CurrentBg = ANSI_TERM_DEFAULT_BKGD_COLOR;
Channel->CurrentFg = ANSI_TERM_DEFAULT_TEXT_COLOR;
//
// Get the output buffer
//
ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
//
// Initialize all the vtutf8 elements to the default state
//
for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++) {
for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
ScreenBuffer->Element[R][C].Value = ' ';
ScreenBuffer->Element[R][C].BgColor = ANSI_TERM_DEFAULT_BKGD_COLOR;
ScreenBuffer->Element[R][C].FgColor = ANSI_TERM_DEFAULT_TEXT_COLOR;
}
}
return STATUS_SUCCESS;
}
NTSTATUS
VTUTF8ChannelCreate(
OUT PSAC_CHANNEL Channel
)
/*++
Routine Description:
This routine allocates a channel and returns a pointer to it.
Arguments:
Channel - The resulting channel.
OpenChannelCmd - All the parameters for the new channel
Return Value:
STATUS_SUCCESS if successful, else the appropriate error code.
--*/
{
NTSTATUS Status;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
do {
//
// Allocate our output buffer
//
Channel->OBuffer = ALLOCATE_POOL(sizeof(SAC_SCREEN_BUFFER), GENERAL_POOL_TAG);
ASSERT(Channel->OBuffer);
if (!Channel->OBuffer) {
Status = STATUS_NO_MEMORY;
break;
}
//
// Allocate our input buffer
//
Channel->IBuffer = (PUCHAR)ALLOCATE_POOL(SAC_RAW_OBUFFER_SIZE, GENERAL_POOL_TAG);
ASSERT(Channel->IBuffer);
if (!Channel->IBuffer) {
Status = STATUS_NO_MEMORY;
break;
}
//
// Initialize the output buffer
//
Status = VTUTF8ChannelOInit(Channel);
if (!NT_SUCCESS(Status)) {
break;
}
//
// Neither buffer has any new data
//
ChannelSetIBufferHasNewData(Channel, FALSE);
ChannelSetOBufferHasNewData(Channel, FALSE);
} while ( FALSE );
//
// Cleanup if necessary
//
if (!NT_SUCCESS(Status)) {
if (Channel->OBuffer) {
FREE_POOL(&Channel->OBuffer);
}
if (Channel->IBuffer) {
FREE_POOL(&Channel->IBuffer);
}
}
return Status;
}
NTSTATUS
VTUTF8ChannelDestroy(
IN OUT PSAC_CHANNEL Channel
)
/*++
Routine Description:
This routine closes a channel.
Arguments:
Channel - The channel to be closed
Return Value:
STATUS_SUCCESS if successful, else the appropriate error code.
--*/
{
NTSTATUS Status;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
//
// Free the dynamically allocated memory
//
if (Channel->OBuffer) {
FREE_POOL(&(Channel->OBuffer));
Channel->OBuffer = NULL;
}
if (Channel->IBuffer) {
FREE_POOL(&(Channel->IBuffer));
Channel->IBuffer = NULL;
}
//
// Now that we've done our channel specific destroy,
// Call the general channel destroy
//
Status = ChannelDestroy(Channel);
return STATUS_SUCCESS;
}
NTSTATUS
VTUTF8ChannelORead(
IN PSAC_CHANNEL Channel,
IN PUCHAR Buffer,
IN ULONG BufferSize,
OUT PULONG ByteCount
)
{
UNREFERENCED_PARAMETER(Channel);
UNREFERENCED_PARAMETER(Buffer);
UNREFERENCED_PARAMETER(ByteCount);
UNREFERENCED_PARAMETER(BufferSize);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
VTUTF8ChannelOEcho(
IN PSAC_CHANNEL Channel,
IN PCUCHAR String,
IN ULONG Size
)
/*++
Routine Description:
This routine puts the string out the ansi port.
Arguments:
Channel - Previously created channel.
String - Output string.
Length - The # of String bytes to process
Return Value:
STATUS_SUCCESS if successful, otherwise status
--*/
{
NTSTATUS Status;
BOOLEAN bStatus;
ULONG Length;
ULONG i;
ULONG k;
ULONG j;
ULONG TranslatedCount;
ULONG UTF8TranslationSize;
PCWSTR pwch;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2);
//
// Note: Simply echoing out the String buffer will ONLY work
// reliably if our VTUTF8 emulation does EXACTLY the same emulation
// as the remote client. If our interpretation of the incoming stream
// differs, there will be a discrepency between the two screen images.
// For instance, if we do line wrapping (col becomes 0, and row++) and
// the remote client does not, echoing of String will fail to reflect
// the line wrapping end users (client) will only see the correct (our)
// representation of our VTUTF8 screen when the switch away and come back,
// thereby causing a screen redraw
//
// One possible way around this problem would be to put 'dirty' bits into our vtutf8 screen
// buffer for each cell. At this point, we could scan the buffer for changes
// and send the appropriate updates rather than just blindly echoing String.
//
//
// Determine the total # of WCHARs to process
//
Length = Size / sizeof(WCHAR);
//
// Do nothing if there is nothing to do
//
if (Length == 0) {
return STATUS_SUCCESS;
}
//
// Point to the beginning of the string
//
pwch = (PCWSTR)String;
//
// Default: we were successful
//
Status = STATUS_SUCCESS;
//
// Divide the incoming buffer into blocks of length
// MAX_UTF8_ENCODE_BLOCK_LENGTH.
//
do {
//
// Determine the remainder
//
k = Length % MAX_UTF8_ENCODE_BLOCK_LENGTH;
if (k > 0) {
//
// Translate the first k characters
//
bStatus = SacTranslateUnicodeToUtf8(
pwch,
k,
Utf8ConversionBuffer,
Utf8ConversionBufferSize,
&UTF8TranslationSize,
&TranslatedCount
);
//
// If this assert hits, it is probably caused by
// a premature NULL termination in the incoming string
//
ASSERT(k == TranslatedCount);
if (!bStatus) {
Status = STATUS_UNSUCCESSFUL;
break;
}
//
// Send the UTF8 encoded characters
//
Status = IoMgrWriteData(
Channel,
(PUCHAR)Utf8ConversionBuffer,
UTF8TranslationSize
);
if (!NT_SUCCESS(Status)) {
break;
}
//
// Adjust the pwch to account for the sent length
//
pwch += k;
}
//
// Determine the # of blocks we can process
//
j = Length / MAX_UTF8_ENCODE_BLOCK_LENGTH;
//
// Translate each WCHAR to UTF8 individually. This way,
// no matter how big the String is, we don't run into
// buffer size problems (it just might take a while).
//
for (i = 0; i < j; i++) {
//
// Encode the next block
//
bStatus = SacTranslateUnicodeToUtf8(
pwch,
MAX_UTF8_ENCODE_BLOCK_LENGTH,
Utf8ConversionBuffer,
Utf8ConversionBufferSize,
&UTF8TranslationSize,
&TranslatedCount
);
//
// If this assert hits, it is probably caused by
// a premature NULL termination in the incoming string
//
ASSERT(MAX_UTF8_ENCODE_BLOCK_LENGTH == TranslatedCount);
ASSERT(UTF8TranslationSize > 0);
if (! bStatus) {
Status = STATUS_UNSUCCESSFUL;
break;
}
//
// Adjust the pwch to account for the sent length
//
pwch += MAX_UTF8_ENCODE_BLOCK_LENGTH;
//
// Send the UTF8 encoded characters
//
Status = IoMgrWriteData(
Channel,
(PUCHAR)Utf8ConversionBuffer,
UTF8TranslationSize
);
if (! NT_SUCCESS(Status)) {
break;
}
}
} while ( FALSE );
//
// Validate that the pwch pointer stopped at the end of the buffer
//
ASSERT(pwch == (PWSTR)(String + Size));
//
// If we were successful, flush the channel's data in the iomgr
//
if (NT_SUCCESS(Status)) {
Status = IoMgrFlushData(Channel);
}
return Status;
}
NTSTATUS
VTUTF8ChannelOWrite(
IN PSAC_CHANNEL Channel,
IN PCUCHAR String,
IN ULONG Size
)
/*++
Routine Description:
This routine takes a string and prints it to the specified channel. If the channel
is the currently active channel, it puts the string out the ansi port as well.
Note: Current Channel lock must be held by caller
Arguments:
Channel - Previously created channel.
String - Output string.
Length - The # of String bytes to process
Return Value:
STATUS_SUCCESS if successful, else the appropriate error code.
--*/
{
NTSTATUS Status;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2);
do {
//
// call the appropriate "printscreen" depending on the channel type
//
// Note: this may be done more cleanly with function pointers and using
// a common function prototype. The ChannelPrintStringIntoScreenBuffer
// function could translate the uchar buffer into a wchar buffer internally
//
Status = VTUTF8ChannelOWrite2(
Channel,
(PCWSTR)String,
Size / sizeof(WCHAR)
);
if (! NT_SUCCESS(Status)) {
break;
}
//
// if the current channel is the active channel and the user has selected
// to display this channel, relay the output directly to the user
//
if (IoMgrIsWriteEnabled(Channel) && ChannelSentToScreen(Channel)){
Status = VTUTF8ChannelOEcho(
Channel,
String,
Size
);
} else {
//
// this is not the current channel,
// hence, this channel has new data
//
ChannelSetOBufferHasNewData(Channel, TRUE);
}
} while ( FALSE );
return Status;
}
NTSTATUS
VTUTF8ChannelOWrite2(
IN PSAC_CHANNEL Channel,
IN PCWSTR String,
IN ULONG Length
)
/*++
Routine Description:
This routine takes a string and prints it into the screen buffer. This makes this
routine, essentially, a VTUTF8 emulator.
Arguments:
Channel - Previously created channel.
String - String to print.
Length - Length of the string to write
Return Value:
STATUS_SUCCESS if successful, else the appropriate error code.
--*/
{
ULONG i;
ULONG Consumed;
ULONG R, C;
PCWSTR pwch;
PSAC_SCREEN_BUFFER ScreenBuffer;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
ASSERT_STATUS(String, STATUS_INVALID_PARAMETER_2);
ASSERT_CHANNEL_ROW_COL(Channel);
//
// Get the VTUTF8 Screen Buffer
//
ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
//
// Iterate through the string and do an internal vtutf8 emulation,
// storing the "screen" in the Screen Buffer
//
for (i = 0; i < Length; i++) {
//
// Get the next character to process
//
pwch = &(String[i]);
if (*pwch == '\033') { // escape char
//
// Note: if the String doesn't contain a complete escape sequence
// then when we consume the escape sequence, we'll fail to
// recognize the sequence and drop it. Then, when the rest
// of the sequence follows, it will appear as text.
//
// FIX: this requires a better overall parsing engine that preserves state...
//
Consumed = VTUTF8ChannelConsumeEscapeSequence(Channel, pwch);
if (Consumed != 0) {
//
// Adding Consumed moves us to just after the escape sequence
// just consumed. However, we need to subract 1 because we
// are about to add one due to the for loop
//
i += Consumed - 1;
continue;
} else {
//
// Ignore the escape
//
i++;
continue;
}
} else {
//
// First, if this is a special character, process it.
//
//
// Return
//
if (*pwch == '\n') {
Channel->CursorCol = 0;
continue;
}
//
// Linefeed
//
if (*pwch == '\r') {
Channel->CursorRow++;
//
// If we scrolled off the bottom, move everything up one line and clear
// the bottom line.
//
if (Channel->CursorRow >= SAC_VTUTF8_ROW_HEIGHT) {
for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT - 1; R++) {
ASSERT(R+1 < SAC_VTUTF8_ROW_HEIGHT);
for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
ScreenBuffer->Element[R][C] = ScreenBuffer->Element[R+1][C];
}
}
ASSERT(R == SAC_VTUTF8_ROW_HEIGHT-1);
for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
RtlZeroMemory(&(ScreenBuffer->Element[R][C]), sizeof(SAC_SCREEN_ELEMENT));
}
Channel->CursorRow--;
}
ASSERT_CHANNEL_ROW_COL(Channel);
continue;
}
//
// Tab
//
if (*pwch == '\t') {
ASSERT_CHANNEL_ROW_COL(Channel);
C = 4 - Channel->CursorCol % 4;
for (; C != 0 ; C--) {
ASSERT_CHANNEL_ROW_COL(Channel);
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Attr = Channel->CurrentAttr;
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].BgColor = Channel->CurrentBg;
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].FgColor = Channel->CurrentFg;
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Value = ' ';
Channel->CursorCol++;
if (Channel->CursorCol >= SAC_VTUTF8_COL_WIDTH) { // no line wrap.
Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
}
}
ASSERT_CHANNEL_ROW_COL(Channel);
continue;
}
//
// Backspace or delete character
//
if ((*pwch == 0x8) || (*pwch == 0x7F)) {
if (Channel->CursorCol > 0) {
Channel->CursorCol--;
}
ASSERT_CHANNEL_ROW_COL(Channel);
continue;
}
//
// We just consume all the rest of non-printable characters.
//
if (*pwch < ' ') {
continue;
}
//
// All normal characters end up here.
//
ASSERT_CHANNEL_ROW_COL(Channel);
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Attr = Channel->CurrentAttr;
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].BgColor = Channel->CurrentBg;
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].FgColor = Channel->CurrentFg;
ScreenBuffer->Element[Channel->CursorRow][Channel->CursorCol].Value = *pwch;
Channel->CursorCol++;
if (Channel->CursorCol == SAC_VTUTF8_COL_WIDTH) { // no line wrap.
Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
}
ASSERT_CHANNEL_ROW_COL(Channel);
}
}
ASSERT_CHANNEL_ROW_COL(Channel);
return STATUS_SUCCESS;
}
//
// This macro calculates the # of escape sequence characters
//
// Note: the compiler accounts for the fact
// that _p and _s are PWCHARs, so we don't need to
// divide by sizeof(WCHAR).
//
#define CALC_CONSUMED(_p,_s)\
((ULONG)((_p) - (_s)) + 1)
ULONG
VTUTF8ChannelConsumeEscapeSequence(
IN PSAC_CHANNEL Channel,
IN PCWSTR String
)
/*++
Routine Description:
This routine takes an escape sequence and process it, returning the number of
character it consumed from the string. If the escape sequence is not a valid
vtutf8 sequence, it returns 0.
Note: if the String doesn't contain a complete escape sequence
then when we consume the escape sequence, we'll fail to
recognize the sequence and drop it. Then, when the rest
of the sequence follows, it will appear as text.
FIX: this requires a better overall parsing engine that preserves state...
Arguments:
Channel - Previously created channel.
String - Escape sequence.
Return Value:
Number of characters consumed
--*/
{
ULONG i;
SAC_ESCAPE_CODE Code;
PCWSTR pch;
ULONG Consumed;
ULONG Param1 = 0;
ULONG Param2 = 0;
ULONG Param3 = 0;
PSAC_SCREEN_BUFFER ScreenBuffer;
ASSERT(String[0] == '\033');
//
// Get the VTUTF8 Screen Buffer
//
ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
//
// Check for one of the easy strings first.
//
for (i = 0; i < sizeof(SacStaticEscapeStrings)/sizeof(SAC_STATIC_ESCAPE_STRING); i++) {
if (wcsncmp(&(String[1]),
SacStaticEscapeStrings[i].String,
SacStaticEscapeStrings[i].StringLength) == 0) {
//
// Populate the arguments for the function to process this code
//
Code = SacStaticEscapeStrings[i].Code;
Param1 = 1;
Param2 = 1;
Param3 = 1;
//
// # chars consumed = length of escape string + <esc>
//
Consumed = SacStaticEscapeStrings[i].StringLength + 1;
goto ProcessCode;
}
}
//
// Check for escape sequences with parameters.
//
if (String[1] != '[') {
return 0;
}
pch = &(String[2]);
//
// look for '<esc>[X' codes
//
switch (*pch) {
case 'A':
Code = CursorUp;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
case 'B':
Code = CursorDown;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
case 'C':
Code = CursorLeft;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
case 'D':
Code = CursorRight;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
case 'K':
Code = ClearToEol;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
}
//
// if we made it here, there should be a # next
//
if (!VTUTF8ChannelScanForNumber(pch, &Param1)) {
return 0;
}
//
// Skip past the numbers
//
while ((*pch >= '0') && (*pch <= '9')) {
pch++;
}
//
// Check for set color
//
if (*pch == 'm') {
switch (Param1) {
case 0:
Code = AttributesOff;
break;
case 1:
Code = BoldOn;
break;
case 5:
Code = BlinkOn;
break;
case 7:
Code = InverseOn;
break;
case 22:
Code = BoldOff;
break;
case 25:
Code = BlinkOff;
break;
case 27:
Code = InverseOff;
break;
default:
if (Param1 >= 40 && Param1 <= 47) {
Code = SetBackgroundColor;
} else if (Param1 >= 30 && Param1 <= 39) {
Code = SetForegroundColor;
} else {
//
// This allows us to catch unhandled codes,
// so we know they need to be supported
//
ASSERT(0);
return 0;
}
break;
}
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
}
if (*pch != ';') {
return 0;
}
pch++;
if (!VTUTF8ChannelScanForNumber(pch, &Param2)) {
return 0;
}
//
// Skip past the numbers
//
while ((*pch >= '0') && (*pch <= '9')) {
pch++;
}
//
// Check for set color
//
if (*pch == 'm') {
Code = SetColor;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
}
//
// Check for set cursor position
//
if (*pch == 'H') {
Code = SetCursorPosition;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
}
if (*pch != ';') {
return 0;
}
pch++;
switch (*pch) {
case 'H':
case 'f':
Code = SetCursorPosition;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
case 'r':
Code = SetScrollRegion;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
}
if (!VTUTF8ChannelScanForNumber(pch, &Param3)) {
return 0;
}
//
// Skip past the numbers
//
while ((*pch >= '0') && (*pch <= '9')) {
pch++;
}
//
// Check for set color and attribute
//
if (*pch == 'm') {
Code = SetColorAndAttribute;
Consumed = CALC_CONSUMED(pch, String);
goto ProcessCode;
}
return 0;
ProcessCode:
ASSERT_CHANNEL_ROW_COL(Channel);
switch (Code) {
case CursorUp:
if (Channel->CursorRow >= Param1) {
Channel->CursorRow = (UCHAR)(Channel->CursorRow - (UCHAR)Param1);
} else {
Channel->CursorRow = 0;
}
ASSERT_CHANNEL_ROW_COL(Channel);
break;
case CursorDown:
if ((Channel->CursorRow + Param1) < SAC_VTUTF8_ROW_HEIGHT) {
Channel->CursorRow = (UCHAR)(Channel->CursorRow + (UCHAR)Param1);
} else {
Channel->CursorRow = SAC_VTUTF8_ROW_HEIGHT - 1;
}
ASSERT_CHANNEL_ROW_COL(Channel);
break;
case CursorLeft:
if (Channel->CursorCol >= Param1) {
Channel->CursorCol = (UCHAR)(Channel->CursorCol - (UCHAR)Param1);
} else {
Channel->CursorCol = 0;
}
ASSERT_CHANNEL_ROW_COL(Channel);
break;
case CursorRight:
if ((Channel->CursorCol + Param1) < SAC_VTUTF8_COL_WIDTH) {
Channel->CursorCol = (UCHAR)(Channel->CursorCol + (UCHAR)Param1);
} else {
Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
}
ASSERT_CHANNEL_ROW_COL(Channel);
break;
case AttributesOff:
//
// Reset to default attributes and colors
//
Channel->CurrentAttr = ANSI_TERM_DEFAULT_ATTRIBUTES;
Channel->CurrentBg = ANSI_TERM_DEFAULT_BKGD_COLOR;
Channel->CurrentFg = ANSI_TERM_DEFAULT_TEXT_COLOR;
break;
case BlinkOn:
Channel->CurrentAttr |= VTUTF8_ATTRIBUTE_BLINK;
break;
case BlinkOff:
Channel->CurrentAttr &= ~VTUTF8_ATTRIBUTE_BLINK;
break;
case BoldOn:
Channel->CurrentAttr |= VTUTF8_ATTRIBUTE_BOLD;
break;
case BoldOff:
Channel->CurrentAttr &= ~VTUTF8_ATTRIBUTE_BOLD;
break;
case InverseOn:
Channel->CurrentAttr |= VTUTF8_ATTRIBUTE_INVERSE;
break;
case InverseOff:
Channel->CurrentAttr &= ~VTUTF8_ATTRIBUTE_INVERSE;
break;
case BackTab:
break;
case ClearToEol:
Param1 = Channel->CursorCol;
Param2 = SAC_VTUTF8_COL_WIDTH;
goto DoClearLine;
case ClearToBol:
Param1 = 0;
Param2 = Channel->CursorCol + 1;
goto DoClearLine;
case ClearLine:
Param1 = 0;
Param2 = SAC_VTUTF8_COL_WIDTH;
DoClearLine:
for (i = Param1; i < Param2; i++) {
ScreenBuffer->Element[Channel->CursorRow][i].Attr = Channel->CurrentAttr;
ScreenBuffer->Element[Channel->CursorRow][i].FgColor = Channel->CurrentFg;
ScreenBuffer->Element[Channel->CursorRow][i].BgColor = Channel->CurrentBg;
ScreenBuffer->Element[Channel->CursorRow][i].Value = ' ';
}
break;
case ClearToEos:
//
// Start with clearing this line from the current cursor position
//
Param3 = Channel->CursorCol;
for (i = Channel->CursorRow; i < SAC_VTUTF8_ROW_HEIGHT; i++) {
for (Param1 = Param3; Param1 < SAC_VTUTF8_COL_WIDTH; Param1++) {
ScreenBuffer->Element[i][Param1].Attr = Channel->CurrentAttr;
ScreenBuffer->Element[i][Param1].FgColor = Channel->CurrentFg;
ScreenBuffer->Element[i][Param1].BgColor = Channel->CurrentBg;
ScreenBuffer->Element[i][Param1].Value = ' ';
}
//
// Then clear the entire line for all other lines
//
Param3 = 0;
}
break;
case ClearToBos:
//
// Start by clearing all of the line
//
Param3 = SAC_VTUTF8_COL_WIDTH;
for (i = 0; i <= Channel->CursorRow; i++) {
if (i == Channel->CursorRow) {
Param3 = Channel->CursorCol;
}
for (Param1 = 0; Param1 < Param3; Param1++) {
ScreenBuffer->Element[i][Param1].Attr = Channel->CurrentAttr;
ScreenBuffer->Element[i][Param1].FgColor = Channel->CurrentFg;
ScreenBuffer->Element[i][Param1].BgColor = Channel->CurrentBg;
ScreenBuffer->Element[i][Param1].Value = ' ';
}
}
break;
case ClearScreen:
for (i = 0; i < SAC_VTUTF8_ROW_HEIGHT; i++) {
for (Param1 = 0; Param1 < SAC_VTUTF8_COL_WIDTH; Param1++) {
ScreenBuffer->Element[i][Param1].Attr = Channel->CurrentAttr;
ScreenBuffer->Element[i][Param1].FgColor = Channel->CurrentFg;
ScreenBuffer->Element[i][Param1].BgColor = Channel->CurrentBg;
ScreenBuffer->Element[i][Param1].Value = ' ';
}
}
break;
case SetCursorPosition:
Channel->CursorRow = (UCHAR)Param1; // I adjust below for 0-based array - don't subtract 1 here.
Channel->CursorCol = (UCHAR)Param2; // I adjust below for 0-based array - don't subtract 1 here.
if (Channel->CursorRow > SAC_VTUTF8_ROW_HEIGHT) {
Channel->CursorRow = SAC_VTUTF8_ROW_HEIGHT;
}
if (Channel->CursorRow >= 1) {
Channel->CursorRow--;
}
if (Channel->CursorCol > SAC_VTUTF8_COL_WIDTH) {
Channel->CursorCol = SAC_VTUTF8_COL_WIDTH;
}
if (Channel->CursorCol >= 1) {
Channel->CursorCol--;
}
ASSERT_CHANNEL_ROW_COL(Channel);
break;
case SetColor:
Channel->CurrentFg = (UCHAR)Param1;
Channel->CurrentBg = (UCHAR)Param2;
break;
case SetBackgroundColor:
Channel->CurrentBg = (UCHAR)Param1;
break;
case SetForegroundColor:
Channel->CurrentFg = (UCHAR)Param1;
break;
case SetColorAndAttribute:
Channel->CurrentAttr = (UCHAR)Param1;
Channel->CurrentFg = (UCHAR)Param2;
Channel->CurrentBg = (UCHAR)Param3;
break;
}
return Consumed;
}
NTSTATUS
VTUTF8ChannelOFlush(
IN PSAC_CHANNEL Channel
)
/*++
Routine Description:
Send the contents of the screen buffer to the remote terminal. This
is done by sending VTUTF8 codes to recreate the screen buffer on the
remote terminal.
Arguments:
Channel - Previously created channel.
Return Value:
STATUS_SUCCESS if successful, else the appropriate error code.
--*/
{
NTSTATUS Status;
BOOLEAN bStatus;
PWCHAR LocalBuffer;
UCHAR CurrentAttr;
UCHAR CurrentFg;
UCHAR CurrentBg;
ULONG R, C;
BOOLEAN RepositionCursor;
ANSI_CMD_SET_COLOR SetColor;
ANSI_CMD_POSITION_CURSOR SetCursor;
PSAC_SCREEN_BUFFER ScreenBuffer;
ULONG TranslatedCount;
ULONG UTF8TranslationSize;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
//
// Get the VTUTF8 Screen Buffer
//
ScreenBuffer = (PSAC_SCREEN_BUFFER)Channel->OBuffer;
//
// Cursor offset on the screen
//
#define CURSOR_ROW_OFFSET 0
#define CURSOR_COL_OFFSET 0
//
// Allocate the local buffer
//
LocalBuffer = ALLOCATE_POOL(20*sizeof(WCHAR), GENERAL_POOL_TAG);
if (!LocalBuffer) {
Status = STATUS_NO_MEMORY;
goto VTUTF8ChannelOFlushCleanup;
}
//
// Clear the terminal screen.
//
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdClearDisplay,
NULL,
0
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
//
// Set the cursor to the top left
//
SetCursor.Y = CURSOR_ROW_OFFSET;
SetCursor.X = CURSOR_COL_OFFSET;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdPositionCursor,
&SetCursor,
sizeof(ANSI_CMD_POSITION_CURSOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
//
// Reset the terminal attributes to defaults
//
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdDisplayAttributesOff,
NULL,
0
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
//
// Send starting attributes
//
CurrentAttr = Channel->CurrentAttr;
Status = VTUTF8ChannelProcessAttributes(
Channel,
CurrentAttr
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
//
// Send starting colors.
//
CurrentBg = Channel->CurrentBg;
CurrentFg = Channel->CurrentFg;
SetColor.BkgColor = CurrentBg;
SetColor.FgColor = CurrentFg;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdSetColor,
&SetColor,
sizeof(ANSI_CMD_SET_COLOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
//
// default: we don't need to reposition the cursor
//
RepositionCursor = FALSE;
//
// Send each character
//
for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++) {
for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) {
if ((ScreenBuffer->Element[R][C].BgColor != CurrentBg) ||
(ScreenBuffer->Element[R][C].FgColor != CurrentFg)) {
//
// Change screen colors as necessary
//
if (RepositionCursor) {
SetCursor.Y = R + CURSOR_ROW_OFFSET;
SetCursor.X = C + CURSOR_COL_OFFSET;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdPositionCursor,
&SetCursor,
sizeof(ANSI_CMD_POSITION_CURSOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
RepositionCursor = FALSE;
}
CurrentBg = ScreenBuffer->Element[R][C].BgColor;
CurrentFg = ScreenBuffer->Element[R][C].FgColor;
SetColor.BkgColor = CurrentBg;
SetColor.FgColor = CurrentFg;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdSetColor,
&SetColor,
sizeof(ANSI_CMD_SET_COLOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
}
if (ScreenBuffer->Element[R][C].Attr != CurrentAttr) {
//
// Change attribute as necessary
//
if (RepositionCursor) {
SetCursor.Y = R + CURSOR_ROW_OFFSET;
SetCursor.X = C + CURSOR_COL_OFFSET;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdPositionCursor,
&SetCursor,
sizeof(ANSI_CMD_POSITION_CURSOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
RepositionCursor = FALSE;
}
CurrentAttr = ScreenBuffer->Element[R][C].Attr;
Status = VTUTF8ChannelProcessAttributes(
Channel,
CurrentAttr
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
}
//
// Send the character. Note: we can optimize the not-sending of
// space characters, if the clear screen was in the same
// color as the current color.
//
#if 0
if ((ScreenBuffer->Element[R][C].Value != ' ') ||
(CurrentAttr != 0) ||
(CurrentBg != ANSI_TERM_DEFAULT_BKGD_COLOR) ||
(CurrentFg != ANSI_TERM_DEFAULT_TEXT_COLOR)) {
#endif
{
if (RepositionCursor) {
SetCursor.Y = R + CURSOR_ROW_OFFSET;
SetCursor.X = C + CURSOR_COL_OFFSET;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdPositionCursor,
&SetCursor,
sizeof(ANSI_CMD_POSITION_CURSOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
RepositionCursor = FALSE;
}
LocalBuffer[0] = ScreenBuffer->Element[R][C].Value;
LocalBuffer[1] = UNICODE_NULL;
bStatus = SacTranslateUnicodeToUtf8(
LocalBuffer,
1,
Utf8ConversionBuffer,
Utf8ConversionBufferSize,
&UTF8TranslationSize,
&TranslatedCount
);
if (! bStatus) {
Status = STATUS_UNSUCCESSFUL;
goto VTUTF8ChannelOFlushCleanup;
}
//
// If the UTF8 encoded string is non-empty, send it
//
if (UTF8TranslationSize > 0) {
Status = IoMgrWriteData(
Channel,
(PUCHAR)Utf8ConversionBuffer,
UTF8TranslationSize
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
}
}
#if 0
} else {
RepositionCursor = TRUE;
}
#endif
}
//
// Position the cursor on the new row
//
RepositionCursor = TRUE;
}
//
// Position cursor
//
SetCursor.Y = Channel->CursorRow + CURSOR_ROW_OFFSET;
SetCursor.X = Channel->CursorCol + CURSOR_COL_OFFSET;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdPositionCursor,
&SetCursor,
sizeof(ANSI_CMD_POSITION_CURSOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
//
// Send current attributes
//
Status = VTUTF8ChannelProcessAttributes(
Channel,
Channel->CurrentAttr
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
//
// Send current colors.
//
SetColor.BkgColor = Channel->CurrentBg;
SetColor.FgColor = Channel->CurrentFg;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdSetColor,
&SetColor,
sizeof(ANSI_CMD_SET_COLOR)
);
if (! NT_SUCCESS(Status)) {
goto VTUTF8ChannelOFlushCleanup;
}
VTUTF8ChannelOFlushCleanup:
//
// If we were successful, flush the channel's data in the iomgr
//
if (NT_SUCCESS(Status)) {
Status = IoMgrFlushData(Channel);
}
//
// Free local resources
//
if (LocalBuffer) {
FREE_POOL(&LocalBuffer);
}
//
// If we have successfully flushed the obuffer,
// then we no longer have any new data
//
if (NT_SUCCESS(Status)) {
ChannelSetOBufferHasNewData(Channel, FALSE);
}
return Status;
}
NTSTATUS
VTUTF8ChannelIWrite(
IN PSAC_CHANNEL Channel,
IN PCUCHAR Buffer,
IN ULONG BufferSize
)
/*++
Routine Description:
This routine takes a single character and adds it to the buffered input for this channel.
Arguments:
Channel - Previously created channel.
Buffer - Incoming buffer of UCHARs
BufferSize - Incoming buffer size
Return Value:
STATUS_SUCCESS if successful, else the appropriate error code.
--*/
{
NTSTATUS Status;
BOOLEAN haveNewChar;
ULONG i;
BOOLEAN IBufferStatus;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
ASSERT_STATUS(Buffer, STATUS_INVALID_PARAMETER_2);
ASSERT_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
//
// Make sure we aren't full
//
Status = VTUTF8ChannelIBufferIsFull(
Channel,
&IBufferStatus
);
if (! NT_SUCCESS(Status)) {
return Status;
}
//
// If there is no more room, then fail
//
if (IBufferStatus == TRUE) {
return STATUS_UNSUCCESSFUL;
}
//
// make sure there is enough room for the buffer
//
// Note: this prevents us from writing a portion of the buffer
// and then failing, leaving the caller in the state where
// it doesn't know how much of the buffer was written.
//
if ((SAC_VTUTF8_IBUFFER_SIZE - VTUTF8ChannelGetIBufferIndex(Channel)) < BufferSize) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// default: we succeeded
//
Status = STATUS_SUCCESS;
for (i = 0; i < BufferSize; i++) {
//
// VTUTF8 channels receive UTF8 encoded Unicode, so
// translate the UTF8 byte by byte into Unicode
// as it's received. Only delcare that we have a new
// Unicode character if the a complete tranlsation
// from UTF8 --> Unicode took place.
//
haveNewChar = SacTranslateUtf8ToUnicode(
Buffer[i],
IncomingUtf8ConversionBuffer,
&IncomingUnicodeValue
);
//
// if a completed Unicode value was assembled, then we have a new character
//
if (haveNewChar) {
PWCHAR pwch;
pwch = (PWCHAR)&(Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)]);
*pwch = IncomingUnicodeValue;
//
// update the buffer index
//
VTUTF8ChannelSetIBufferIndex(
Channel,
VTUTF8ChannelGetIBufferIndex(Channel) + sizeof(WCHAR)/sizeof(UCHAR)
);
}
}
//
// Fire the Has New Data event if specified
//
if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) {
ASSERT(Channel->HasNewDataEvent);
ASSERT(Channel->HasNewDataEventObjectBody);
ASSERT(Channel->HasNewDataEventWaitObjectBody);
KeSetEvent(
Channel->HasNewDataEventWaitObjectBody,
EVENT_INCREMENT,
FALSE
);
}
return STATUS_SUCCESS;
}
NTSTATUS
VTUTF8ChannelIRead(
IN PSAC_CHANNEL Channel,
IN PUCHAR Buffer,
IN ULONG BufferSize,
OUT PULONG ByteCount
)
/*++
Routine Description:
This routine takes the first character in the input buffer, removes and returns it. If
there is none, it returns 0x0.
Arguments:
Channel - Previously created channel.
Buffer - The buffer to read into
BufferSize - The size of the buffer
ByteCount - The # of bytes read
Return Value:
Status
--*/
{
ULONG CopyChars;
ULONG CopySize;
//
// initialize
//
CopyChars = 0;
CopySize = 0;
//
// Default: no bytes were read
//
*ByteCount = 0;
//
// Bail if there is no new data
//
if (Channel->IBufferLength(Channel) == 0) {
ASSERT(ChannelHasNewIBufferData(Channel) == FALSE);
return STATUS_SUCCESS;
}
//
// Caclulate the largest buffer size we can use (and need), and then calculate
// the number of characters this refers to.
//
CopySize = Channel->IBufferLength(Channel) * sizeof(WCHAR);
CopySize = CopySize > BufferSize ? BufferSize : CopySize;
CopyChars = CopySize / sizeof(WCHAR);
//
// recalc size in case there was rounding when calculating CopyChars
//
CopySize = CopyChars * sizeof(WCHAR);
//
// sanity check the copy size
//
ASSERT(CopyChars <= Channel->IBufferLength(Channel));
//
// Copy as much as we can from the ibuffer to the out-going buffer
//
RtlCopyMemory(Buffer, Channel->IBuffer, CopySize);
//
// Update the buffer index to account for the size we just copied
//
VTUTF8ChannelSetIBufferIndex(
Channel,
VTUTF8ChannelGetIBufferIndex(Channel) - CopySize
);
//
// If there is remaining data left in the Channel input buffer,
// shift it to the beginning
//
if (Channel->IBufferLength(Channel) > 0) {
RtlMoveMemory(&(Channel->IBuffer[0]),
&(Channel->IBuffer[CopySize]),
Channel->IBufferLength(Channel) * sizeof(WCHAR)
);
}
//
// Send back the # of bytes read
//
*ByteCount = CopySize;
return STATUS_SUCCESS;
}
BOOLEAN
VTUTF8ChannelScanForNumber(
IN PCWSTR pch,
OUT PULONG Number
)
/*++
Routine Description:
This routine takes a character stream and converts it into an integer.
Arguments:
pch - The character stream.
Number - The equivalent integer.
Return Value:
TRUE, if successful, else FALSE.
--*/
{
if ((*pch < '0') || (*pch > '9')) {
return FALSE;
}
*Number = 0;
while ((*pch >= '0') && (*pch <= '9')) {
*Number = *Number * 10;
*Number = *Number + (ULONG)(*pch - '0');
pch++;
}
return TRUE;
}
NTSTATUS
VTUTF8ChannelIBufferIsFull(
IN PSAC_CHANNEL Channel,
OUT BOOLEAN* BufferStatus
)
/*++
Routine Description:
Determine if the IBuffer is full
Arguments:
Channel - Previously created channel.
BufferStatus - on exit, TRUE if the buffer is full, otherwise FALSE
Return Value:
Status
--*/
{
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
*BufferStatus = (BOOLEAN)(VTUTF8ChannelGetIBufferIndex(Channel) >= (SAC_VTUTF8_IBUFFER_SIZE-1));
return STATUS_SUCCESS;
}
WCHAR
VTUTF8ChannelIReadLast(
IN PSAC_CHANNEL Channel
)
/*++
Routine Description:
This routine takes the last character in the input buffer, removes and returns it. If
there is none, it returns 0x0.
Arguments:
Channel - Previously created channel.
Return Value:
Last character in the input buffer.
--*/
{
WCHAR Char;
PWCHAR pwch;
ASSERT(Channel);
Char = UNICODE_NULL;
if (Channel->IBufferLength(Channel) > 0) {
VTUTF8ChannelSetIBufferIndex(
Channel,
VTUTF8ChannelGetIBufferIndex(Channel) - sizeof(WCHAR)/sizeof(UCHAR)
);
pwch = (PWCHAR)&Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)];
Char = *pwch;
*pwch = UNICODE_NULL;
}
return Char;
}
ULONG
VTUTF8ChannelIBufferLength(
IN PSAC_CHANNEL Channel
)
/*++
Routine Description:
This routine determines the length of the input buffer, treating the input buffer
contents as a string
Arguments:
Channel - Previously created channel.
Return Value:
The length of the current input buffer
--*/
{
ASSERT(Channel);
return (VTUTF8ChannelGetIBufferIndex(Channel) / sizeof(WCHAR));
}
NTSTATUS
VTUTF8ChannelAnsiDispatch(
IN PSAC_CHANNEL Channel,
IN ANSI_CMD Command,
IN PVOID InputBuffer OPTIONAL,
IN SIZE_T InputBufferSize OPTIONAL
)
/*++
Routine Description:
Arguments:
Channel - The channel sending this escape sequence
Command - The command to execute.
Environment:
Status
--*/
{
NTSTATUS Status;
PUCHAR Tmp;
PUCHAR LocalBuffer;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER_1);
//
// default: not using local buffer
//
LocalBuffer = NULL;
Tmp = NULL;
//
// Default: we succeeded
//
Status = STATUS_SUCCESS;
//
// Various output commands
//
switch (Command) {
case ANSICmdClearDisplay:
Tmp = (PUCHAR)"\033[2J";
break;
case ANSICmdClearToEndOfDisplay:
Tmp = (PUCHAR)"\033[0J";
break;
case ANSICmdClearToEndOfLine:
Tmp = (PUCHAR)"\033[0K";
break;
case ANSICmdDisplayAttributesOff:
Tmp = (PUCHAR)"\033[0m";
break;
case ANSICmdDisplayInverseVideoOn:
Tmp = (PUCHAR)"\033[7m";
break;
case ANSICmdDisplayInverseVideoOff:
Tmp = (PUCHAR)"\033[27m";
break;
case ANSICmdDisplayBlinkOn:
Tmp = (PUCHAR)"\033[5m";
break;
case ANSICmdDisplayBlinkOff:
Tmp = (PUCHAR)"\033[25m";
break;
case ANSICmdDisplayBoldOn:
Tmp = (PUCHAR)"\033[1m";
break;
case ANSICmdDisplayBoldOff:
Tmp = (PUCHAR)"\033[22m";
break;
case ANSICmdSetColor:
case ANSICmdPositionCursor: {
ULONG l;
//
// allocate tmp buffer
//
LocalBuffer = ALLOCATE_POOL(80*sizeof(UCHAR), GENERAL_POOL_TAG);
ASSERT_STATUS(LocalBuffer, STATUS_NO_MEMORY);
switch (Command) {
case ANSICmdSetColor:
if ((InputBuffer == NULL) ||
(InputBufferSize != sizeof(ANSI_CMD_SET_COLOR))) {
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Assemble set color command
//
#if 0
l = sprintf((LPSTR)LocalBuffer,
"\033[%d;%dm",
((PANSI_CMD_SET_COLOR)InputBuffer)->BkgColor,
((PANSI_CMD_SET_COLOR)InputBuffer)->FgColor
);
#else
//
// Break the color commands into to two commands.
//
// Note: we do this because this is much more likely
// to be implemented than the compound command.
//
l = sprintf((LPSTR)LocalBuffer,
"\033[%dm\033[%dm",
((PANSI_CMD_SET_COLOR)InputBuffer)->BkgColor,
((PANSI_CMD_SET_COLOR)InputBuffer)->FgColor
);
#endif
ASSERT((l+1)*sizeof(UCHAR) < 80);
Tmp = &(LocalBuffer[0]);
break;
case ANSICmdPositionCursor:
if ((InputBuffer == NULL) ||
(InputBufferSize != sizeof(ANSI_CMD_POSITION_CURSOR))) {
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Assemble position cursor command
//
l = sprintf((LPSTR)LocalBuffer,
"\033[%d;%dH",
((PANSI_CMD_POSITION_CURSOR)InputBuffer)->Y + 1,
((PANSI_CMD_POSITION_CURSOR)InputBuffer)->X + 1
);
ASSERT((l+1)*sizeof(UCHAR) < 80);
Tmp = &(LocalBuffer[0]);
break;
default:
Status = STATUS_INVALID_PARAMETER;
ASSERT(0);
break;
}
break;
}
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Send the data if we were successful
//
if (NT_SUCCESS(Status)) {
ASSERT(Tmp);
if (Tmp) {
Status = IoMgrWriteData(
Channel,
Tmp,
(ULONG)(strlen((const char *)Tmp)*sizeof(UCHAR))
);
}
//
// If we were successful, flush the channel's data in the iomgr
//
if (NT_SUCCESS(Status)) {
Status = IoMgrFlushData(Channel);
}
}
if (LocalBuffer) {
FREE_POOL(&LocalBuffer);
}
return Status;
}
NTSTATUS
VTUTF8ChannelProcessAttributes(
IN PSAC_CHANNEL Channel,
IN UCHAR Attributes
)
/*++
Routine Description:
Arguments:
Returns:
--*/
{
NTSTATUS Status;
ANSI_CMD Cmd;
ASSERT_STATUS(Channel, STATUS_INVALID_PARAMETER);
do {
#if 0
//
// Send the attributes off command
//
// Note: if this attribute is set,
// then we ignore the rest of the
// attributes
//
if (Attributes == VTUTF8_ATTRIBUTES_OFF) {
Status = VTUTF8ChannelAnsiDispatch(
Channel,
ANSICmdDisplayAttributesOff,
NULL,
0
);
if (! NT_SUCCESS(Status)) {
break;
}
//
// There are no more attributes to check
//
break;
}
#endif
//
// Bold
//
Cmd = Attributes & VTUTF8_ATTRIBUTE_BOLD ?
ANSICmdDisplayBoldOn :
ANSICmdDisplayBoldOff;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
Cmd,
NULL,
0
);
if (! NT_SUCCESS(Status)) {
break;
}
//
// Blink
//
Cmd = Attributes & VTUTF8_ATTRIBUTE_BLINK ?
ANSICmdDisplayBlinkOn :
ANSICmdDisplayBlinkOff;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
Cmd,
NULL,
0
);
if (! NT_SUCCESS(Status)) {
break;
}
//
// Inverse video
//
Cmd = Attributes & VTUTF8_ATTRIBUTE_INVERSE ?
ANSICmdDisplayInverseVideoOn :
ANSICmdDisplayInverseVideoOff;
Status = VTUTF8ChannelAnsiDispatch(
Channel,
Cmd,
NULL,
0
);
if (! NT_SUCCESS(Status)) {
break;
}
} while ( FALSE );
return Status;
}
ULONG
VTUTF8ChannelGetIBufferIndex(
IN PSAC_CHANNEL Channel
)
/*++
Routine Description:
Get teh ibuffer index
Arguments:
Channel - the channel to get the ibuffer index from
Environment:
The ibuffer index
--*/
{
ASSERT(Channel);
//
// Make sure the ibuffer index is atleast aligned to a WCHAR
//
ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
//
// Make sure the ibuffer index is in bounds
//
ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
return Channel->IBufferIndex;
}
VOID
VTUTF8ChannelSetIBufferIndex(
IN PSAC_CHANNEL Channel,
IN ULONG IBufferIndex
)
/*++
Routine Description:
Set the ibuffer index
Arguments:
Channel - the channel to get the ibuffer index from
IBufferIndex - the new inbuffer index
Environment:
None
--*/
{
ASSERT(Channel);
//
// Make sure the ibuffer index is atleast aligned to a WCHAR
//
ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
//
// Make sure the ibuffer index is in bounds
//
ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
//
// Set the index
//
Channel->IBufferIndex = IBufferIndex;
//
// Set the has new data flag accordingly
//
ChannelSetIBufferHasNewData(
Channel,
Channel->IBufferIndex == 0 ? FALSE : TRUE
);
//
// Additional checking if the index == 0
//
if (Channel->IBufferIndex == 0) {
//
// Clear the Has New Data event if specified
//
if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) {
ASSERT(Channel->HasNewDataEvent);
ASSERT(Channel->HasNewDataEventObjectBody);
ASSERT(Channel->HasNewDataEventWaitObjectBody);
KeClearEvent(Channel->HasNewDataEventWaitObjectBody);
}
}
}