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.
 
 
 
 
 
 

5180 lines
176 KiB

/*++
Copyright (c) 1985 - 1999, Microsoft Corporation
Module Name:
cmdline.c
Abstract:
This file implements command line editing and aliasing.
Author:
Therese Stowell (thereses) 22-Mar-1991
Revision History:
Notes:
The input model for the command line editing popups is complex.
Here is the relevant pseudocode:
CookedReadWaitRoutine
if (CookedRead->Popup)
Status = (*CookedRead->Popup->PopupInputRoutine)();
if (Status == CONSOLE_STATUS_READ_COMPLETE)
return STATUS_SUCCESS;
return Status;
CookedRead
if (Command Line Editing Key)
ProcessCommandLine
else
process regular key
ProcessCommandLine
if F7
return CommandLinePopup
CommandLinePopup
draw popup
return ProcessCommandListInput
ProcessCommandListInput
while (TRUE)
GetChar
if (wait)
return wait
switch (char)
.
.
.
--*/
#include "precomp.h"
#pragma hdrstop
#define COPY_TO_CHAR_PROMPT_LENGTH 26
#define COPY_FROM_CHAR_PROMPT_LENGTH 28
#define COMMAND_NUMBER_PROMPT_LENGTH 22
#define COMMAND_NUMBER_LENGTH 5
#define MINIMUM_COMMAND_PROMPT_SIZE COMMAND_NUMBER_LENGTH
#if defined(FE_SB)
#define CHAR_COUNT(cch) cch
#else
#define CHAR_COUNT(cch) ((cch)/sizeof(WCHAR))
#endif
#define ALT_PRESSED (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
#define CTRL_PRESSED (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
#define CTRL_BUT_NOT_ALT(n) \
(((n) & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && \
!((n) & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))
//
// Extended Edit Key
//
ExtKeyDefTable gaKeyDef;
CONST ExtKeyDefTable gaDefaultKeyDef = {
{ // A
0, VK_HOME, 0, // Ctrl
LEFT_CTRL_PRESSED, VK_HOME, 0, // Alt
0, 0, 0, // Ctrl+Alt
},
{ // B
0, VK_LEFT, 0, // Ctrl
LEFT_CTRL_PRESSED, VK_LEFT, 0, // Alt
},
{ // C
0,
},
{ // D
0, VK_DELETE, 0, // Ctrl
LEFT_CTRL_PRESSED, VK_DELETE, 0, // Alt
0, 0, 0, // Ctrl+Alt
},
{ // E
0, VK_END, 0, // Ctrl
LEFT_CTRL_PRESSED, VK_END, 0, // Alt
0, 0, 0, // Ctrl+Alt
},
{ // F
0, VK_RIGHT, 0, // Ctrl
LEFT_CTRL_PRESSED, VK_RIGHT, 0, // Alt
0, 0, 0, // Ctrl+Alt
},
{ // G
0,
},
{ // H
0,
},
{ // I
0,
},
{ // J
0,
},
{ // K
LEFT_CTRL_PRESSED, VK_END, 0, // Ctrl
},
{ // L
0,
},
{ // M
0,
},
{ // N
0, VK_DOWN, 0, // Ctrl
},
{ // O
0,
},
{ // P
0, VK_UP, 0, // Ctrl
},
{ // Q
0,
},
{ // R
0, VK_F8, 0, // Ctrl
},
{ // S
0, VK_PAUSE, 0, // Ctrl
},
{ // T
LEFT_CTRL_PRESSED, VK_DELETE, 0, // Ctrl
},
{ // U
0, VK_ESCAPE, 0, // Ctrl
},
{ // V
0,
},
{ // W
LEFT_CTRL_PRESSED, VK_BACK, EXTKEY_ERASE_PREV_WORD, // Ctrl
},
{ // X
0,
},
{ // Y
0,
},
{ // Z
0,
},
};
//
// InitExtendedEditKeys
//
// Initialize the extended edit key table.
// If pKeyDefbuf is NULL, the internal default table is used.
// Otherwise, lpbyte should point to a valid ExtKeyDefBuf.
//
VOID InitExtendedEditKeys(CONST ExtKeyDefBuf* pKeyDefBuf)
{
CONST BYTE* lpbyte;
int i;
DWORD dwCheckSum;
//
// Sanity check
// If pKeyDefBuf is NULL, give it the default value.
// If the version is not supported, just use the default and bail.
//
if (pKeyDefBuf == NULL || pKeyDefBuf->dwVersion != 0) {
#if DBG
if (pKeyDefBuf != NULL) {
DbgPrint("InitExtendedEditKeys: Unsupported version number(%d)\n", pKeyDefBuf->dwVersion);
}
#endif
retry_clean:
memcpy(gaKeyDef, gaDefaultKeyDef, sizeof gaKeyDef);
return;
}
//
// Calculate check sum
//
dwCheckSum = 0;
for (lpbyte = (CONST BYTE*)pKeyDefBuf, i = FIELD_OFFSET(ExtKeyDefBuf, table); i < sizeof *pKeyDefBuf; ++i) {
dwCheckSum += lpbyte[i];
}
if (dwCheckSum != pKeyDefBuf->dwCheckSum) {
#if DBG
DbgPrint("InitExtendedEditKeys: Checksum(%d) does not match.\n", pKeyDefBuf->dwCheckSum);
#endif
goto retry_clean;
}
//
// Copy the entity
//
memcpy(gaKeyDef, pKeyDefBuf->table, sizeof gaKeyDef);
}
CONST ExtKeySubst* ParseEditKeyInfo(IN OUT PKEY_EVENT_RECORD pKeyEvent)
{
CONST ExtKeyDef* pKeyDef;
CONST ExtKeySubst* pKeySubst;
//
// If not extended mode, or Control key or Alt key is not pressed,
// or virtual keycode is out of range, just bail.
//
if (!gExtendedEditKey ||
(pKeyEvent->dwControlKeyState & (CTRL_PRESSED | ALT_PRESSED)) == 0 ||
pKeyEvent->wVirtualKeyCode < 'A' || pKeyEvent->wVirtualKeyCode > 'Z') {
return NULL;
}
//
// Get the corresponding KeyDef.
//
pKeyDef = &gaKeyDef[pKeyEvent->wVirtualKeyCode - 'A'];
//
// Get the KeySubst based on the modifier status.
//
if (pKeyEvent->dwControlKeyState & ALT_PRESSED) {
if (pKeyEvent->dwControlKeyState & CTRL_PRESSED) {
pKeySubst = &pKeyDef->keys[2];
} else {
pKeySubst = &pKeyDef->keys[1];
}
} else {
UserAssert(pKeyEvent->dwControlKeyState & CTRL_PRESSED);
pKeySubst = &pKeyDef->keys[0];
}
UserAssert(pKeySubst);
//
// If the conbination is not defined, just bail.
//
if (pKeySubst->wVirKey == 0) {
return NULL;
}
//
// Substitute the input with ext key.
//
pKeyEvent->dwControlKeyState = pKeySubst->wMod;
pKeyEvent->wVirtualKeyCode = pKeySubst->wVirKey;
pKeyEvent->uChar.UnicodeChar = pKeySubst->wUnicodeChar;
return pKeySubst;
}
//
// IsPauseKey
// returns TRUE if pKeyEvent is pause.
// The default key is Ctrl-S if extended edit keys are not specified.
//
BOOL IsPauseKey(IN PKEY_EVENT_RECORD pKeyEvent)
{
if (gExtendedEditKey) {
KEY_EVENT_RECORD KeyEvent = *pKeyEvent;
CONST ExtKeySubst* pKeySubst = ParseEditKeyInfo(&KeyEvent);
return pKeySubst != NULL && pKeySubst->wVirKey == VK_PAUSE;
}
return pKeyEvent->wVirtualKeyCode == L'S' && CTRL_BUT_NOT_ALT(pKeyEvent->dwControlKeyState);
}
//
// Word delimiters
//
WCHAR gaWordDelimChars[WORD_DELIM_MAX];
CONST WCHAR gaWordDelimCharsDefault[WORD_DELIM_MAX] = L"\\" L"+!:=/.<>;|&";
BOOL IsWordDelim(WCHAR wch)
{
int i;
//
// Before it reaches here, L' ' case should have beeen already detected,
// and gaWordDelimChars is specified.
//
UserAssert(wch != L' ' && gaWordDelimChars[0]);
for (i = 0; gaWordDelimChars[i] && i < WORD_DELIM_MAX; ++i) {
if (wch == gaWordDelimChars[i]) {
return TRUE;
}
}
return FALSE;
}
PEXE_ALIAS_LIST
AddExeAliasList(
IN PCONSOLE_INFORMATION Console,
IN LPVOID ExeName,
IN USHORT ExeLength, // in bytes
IN BOOLEAN UnicodeExe
)
{
PEXE_ALIAS_LIST AliasList;
AliasList = ConsoleHeapAlloc(ALIAS_TAG, sizeof(EXE_ALIAS_LIST));
if (AliasList == NULL) {
return NULL;
}
if (UnicodeExe) {
AliasList->ExeName = ConsoleHeapAlloc(ALIAS_TAG, ExeLength);
if (AliasList->ExeName == NULL) {
ConsoleHeapFree(AliasList);
return NULL;
}
RtlCopyMemory(AliasList->ExeName,ExeName,ExeLength);
AliasList->ExeLength = ExeLength;
} else {
AliasList->ExeName = ConsoleHeapAlloc(ALIAS_TAG, ExeLength*sizeof(WCHAR));
if (AliasList->ExeName == NULL) {
ConsoleHeapFree(AliasList);
return NULL;
}
AliasList->ExeLength = (USHORT)ConvertInputToUnicode(Console->CP,
ExeName,
ExeLength,
AliasList->ExeName,
ExeLength);
AliasList->ExeLength *= 2;
}
InitializeListHead(&AliasList->AliasList);
InsertHeadList(&Console->ExeAliasList,&AliasList->ListLink);
return AliasList;
}
int
MyStringCompareW(
IN LPWSTR Str1,
IN LPWSTR Str2,
IN USHORT Length, // in bytes
IN BOOLEAN bCaseInsensitive
)
{
UNICODE_STRING String1;
UNICODE_STRING String2;
String1.Length = Length;
String1.MaximumLength = Length;
String1.Buffer = Str1;
String2.Length = Length;
String2.MaximumLength = Length;
String2.Buffer = Str2;
return RtlCompareUnicodeString(&String1,
&String2,
bCaseInsensitive);
}
#define my_wcsncmpi(p1, p2, n) MyStringCompareW(p1, p2, n, TRUE)
#define my_wcsncmp(p1, p2, n) MyStringCompareW(p1, p2, n, FALSE)
PEXE_ALIAS_LIST
FindExe(
IN PCONSOLE_INFORMATION Console,
IN LPVOID ExeName,
IN USHORT ExeLength, // in bytes
IN BOOLEAN UnicodeExe
)
/*++
This routine searches for the specified exe alias list. It returns
a pointer to the exe list if found, NULL if not found.
--*/
{
PEXE_ALIAS_LIST AliasList;
PLIST_ENTRY ListHead, ListNext;
LPWSTR UnicodeExeName;
if (UnicodeExe) {
UnicodeExeName = ExeName;
} else {
UnicodeExeName = ConsoleHeapAlloc(TMP_TAG, ExeLength * sizeof(WCHAR));
if (UnicodeExeName == NULL)
return NULL;
ExeLength = (USHORT)ConvertInputToUnicode(Console->CP,
ExeName,
ExeLength,
UnicodeExeName,
ExeLength);
ExeLength *= 2;
}
ListHead = &Console->ExeAliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
if (AliasList->ExeLength == ExeLength &&
!my_wcsncmpi(AliasList->ExeName,UnicodeExeName,ExeLength)) {
if (!UnicodeExe) {
ConsoleHeapFree(UnicodeExeName);
}
return AliasList;
}
ListNext = ListNext->Flink;
}
if (!UnicodeExe) {
ConsoleHeapFree(UnicodeExeName);
}
return NULL;
}
PALIAS
FindAlias(
IN PEXE_ALIAS_LIST AliasList,
IN LPWSTR AliasName,
IN USHORT AliasLength // in bytes
)
/*++
This routine searches for the specified alias. If it finds one,
it moves it to the head of the list and returns a pointer to the
alias. Otherwise it returns NULL.
--*/
{
PALIAS Alias;
PLIST_ENTRY ListHead, ListNext;
ListHead = &AliasList->AliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
if (Alias->SourceLength == AliasLength &&
!my_wcsncmpi(Alias->Source,AliasName,AliasLength)) {
if (ListNext != ListHead->Flink) {
RemoveEntryList(ListNext);
InsertHeadList(ListHead,ListNext);
}
return Alias;
}
ListNext = ListNext->Flink;
}
return NULL;
}
NTSTATUS
AddAlias(
IN PEXE_ALIAS_LIST ExeAliasList,
IN LPWSTR Source,
IN USHORT SourceLength, // in bytes
IN LPWSTR Target,
IN USHORT TargetLength // in bytes
)
/*++
This routine creates an alias and inserts it into the exe alias list.
--*/
{
PALIAS Alias;
Alias = ConsoleHeapAlloc(ALIAS_TAG, sizeof(ALIAS));
if (Alias == NULL) {
return STATUS_NO_MEMORY;
}
Alias->Source = ConsoleHeapAlloc(ALIAS_TAG, SourceLength);
if (Alias->Source == NULL) {
ConsoleHeapFree(Alias);
return STATUS_NO_MEMORY;
}
Alias->Target = ConsoleHeapAlloc(ALIAS_TAG, TargetLength);
if (Alias->Target == NULL) {
ConsoleHeapFree(Alias->Source);
ConsoleHeapFree(Alias);
return STATUS_NO_MEMORY;
}
Alias->SourceLength = SourceLength;
Alias->TargetLength = TargetLength;
RtlCopyMemory(Alias->Source,Source,SourceLength);
RtlCopyMemory(Alias->Target,Target,TargetLength);
InsertHeadList(&ExeAliasList->AliasList,&Alias->ListLink);
return STATUS_SUCCESS;
}
NTSTATUS
ReplaceAlias(
IN PALIAS Alias,
IN LPWSTR Target,
IN USHORT TargetLength // in bytes
)
/*++
This routine replaces an existing target with a new target.
--*/
{
LPWSTR NewTarget;
NewTarget = ConsoleHeapAlloc(ALIAS_TAG, TargetLength);
if (NewTarget == NULL) {
return STATUS_NO_MEMORY;
}
ConsoleHeapFree(Alias->Target);
Alias->Target = NewTarget;
Alias->TargetLength = TargetLength;
RtlCopyMemory(Alias->Target,Target,TargetLength);
return STATUS_SUCCESS;
}
NTSTATUS
RemoveAlias(
IN PALIAS Alias
)
/*++
This routine removes an alias.
--*/
{
RemoveEntryList(&Alias->ListLink);
ConsoleHeapFree(Alias->Source);
ConsoleHeapFree(Alias->Target);
ConsoleHeapFree(Alias);
return STATUS_SUCCESS;
}
VOID
FreeAliasList(
IN PEXE_ALIAS_LIST ExeAliasList
)
{
PLIST_ENTRY ListHead, ListNext;
PALIAS Alias;
ListHead = &ExeAliasList->AliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
ListNext = ListNext->Flink;
RemoveAlias(Alias);
}
RemoveEntryList(&ExeAliasList->ListLink);
ConsoleHeapFree(ExeAliasList->ExeName);
ConsoleHeapFree(ExeAliasList);
}
VOID
FreeAliasBuffers(
IN PCONSOLE_INFORMATION Console
)
{
PEXE_ALIAS_LIST AliasList;
PLIST_ENTRY ListHead, ListNext;
ListHead = &Console->ExeAliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
ListNext = ListNext->Flink;
FreeAliasList(AliasList);
}
}
ULONG
SrvAddConsoleAlias(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
/*++
Routine Description:
This routine adds a command line alias to the global set.
Arguments:
m - message containing api parameters
ReplyStatus - Indicates whether to reply to the dll port.
Return Value:
--*/
{
PCONSOLE_ADDALIAS_MSG a = (PCONSOLE_ADDALIAS_MSG)&m->u.ApiMessageData;
PALIAS Alias;
PCONSOLE_INFORMATION Console;
PEXE_ALIAS_LIST ExeAliasList;
NTSTATUS Status;
LPWSTR Source,Target;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Source, a->SourceLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->Target,a->TargetLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
if (a->Unicode) {
Source = a->Source;
Target = a->Target;
} else {
Source = ConsoleHeapAlloc(TMP_TAG, a->SourceLength * sizeof(WCHAR));
if (Source == NULL) {
UnlockConsole(Console);
return STATUS_NO_MEMORY;
}
Target = ConsoleHeapAlloc(TMP_TAG, a->TargetLength * sizeof(WCHAR));
if (Target == NULL) {
ConsoleHeapFree(Source);
UnlockConsole(Console);
return STATUS_NO_MEMORY;
}
a->SourceLength = (USHORT)ConvertInputToUnicode(Console->CP,
a->Source,
a->SourceLength,
Source,
a->SourceLength);
a->SourceLength *= 2;
a->TargetLength = (USHORT)ConvertInputToUnicode(Console->CP,
a->Target,
a->TargetLength,
Target,
a->TargetLength);
a->TargetLength *= 2;
}
//
// find specified exe. if it's not there, add it if we're not
// removing an alias.
//
ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
if (ExeAliasList) {
Alias = FindAlias(ExeAliasList,Source,a->SourceLength);
if (a->TargetLength) {
if (Alias) {
Status = ReplaceAlias(Alias,
Target,
a->TargetLength);
} else {
Status = AddAlias(ExeAliasList,
Source,
a->SourceLength,
Target,
a->TargetLength);
}
} else {
if (Alias) {
Status = RemoveAlias(Alias);
}
}
} else {
if (a->TargetLength) {
ExeAliasList = AddExeAliasList(Console,a->Exe,a->ExeLength,a->UnicodeExe);
if (ExeAliasList) {
Status = AddAlias(ExeAliasList,
Source,
a->SourceLength,
Target,
a->TargetLength);
} else {
Status = STATUS_NO_MEMORY;
}
}
}
UnlockConsole(Console);
if (!a->Unicode) {
ConsoleHeapFree(Source);
ConsoleHeapFree(Target);
}
return Status;
UNREFERENCED_PARAMETER(ReplyStatus);
}
ULONG
SrvGetConsoleAlias(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
/*++
Routine Description:
This routine get a command line alias from the global set.
Arguments:
m - message containing api parameters
ReplyStatus - Indicates whether to reply to the dll port.
Return Value:
--*/
{
NTSTATUS Status;
PCONSOLE_GETALIAS_MSG a = (PCONSOLE_GETALIAS_MSG)&m->u.ApiMessageData;
PALIAS Alias;
PCONSOLE_INFORMATION Console;
PEXE_ALIAS_LIST ExeAliasList;
LPWSTR Source,Target;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Source, a->SourceLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->Target, a->TargetLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
if (a->Unicode) {
Source = a->Source;
Target = a->Target;
} else {
Source = ConsoleHeapAlloc(TMP_TAG, a->SourceLength * sizeof(WCHAR));
if (Source == NULL) {
UnlockConsole(Console);
return STATUS_NO_MEMORY;
}
Target = ConsoleHeapAlloc(TMP_TAG, a->TargetLength * sizeof(WCHAR));
if (Target == NULL) {
ConsoleHeapFree(Source);
UnlockConsole(Console);
return STATUS_NO_MEMORY;
}
a->TargetLength = (USHORT)(a->TargetLength * sizeof(WCHAR));
a->SourceLength = (USHORT)ConvertInputToUnicode(Console->CP,
a->Source,
a->SourceLength,
Source,
a->SourceLength);
a->SourceLength *= 2;
}
ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
if (ExeAliasList) {
Alias = FindAlias(ExeAliasList,Source,a->SourceLength);
if (Alias) {
if (Alias->TargetLength + sizeof(WCHAR) > a->TargetLength) {
Status = STATUS_BUFFER_TOO_SMALL;
} else {
a->TargetLength = Alias->TargetLength + sizeof(WCHAR);
RtlCopyMemory(Target,Alias->Target,Alias->TargetLength);
Target[Alias->TargetLength/sizeof(WCHAR)] = L'\0';
}
} else {
Status = STATUS_UNSUCCESSFUL;
}
} else {
Status = STATUS_UNSUCCESSFUL;
}
if (!a->Unicode) {
if (NT_SUCCESS(Status)) {
a->TargetLength = (USHORT)ConvertToOem(Console->CP,
Target,
a->TargetLength / sizeof(WCHAR),
a->Target,
CHAR_COUNT(a->TargetLength)
);
}
ConsoleHeapFree(Source);
ConsoleHeapFree(Target);
}
UnlockConsole(Console);
return Status;
UNREFERENCED_PARAMETER(ReplyStatus);
}
DWORD
SrvGetConsoleAliasesLength(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_GETALIASESLENGTH_MSG a = (PCONSOLE_GETALIASESLENGTH_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
PEXE_ALIAS_LIST ExeAliasList;
PALIAS Alias;
PLIST_ENTRY ListHead, ListNext;
NTSTATUS Status;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
a->AliasesLength = 0;
ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
if (ExeAliasList) {
ListHead = &ExeAliasList->AliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
a->AliasesLength += Alias->SourceLength + Alias->TargetLength + (2*sizeof(WCHAR)); // +2 is for = and term null
ListNext = ListNext->Flink;
}
}
if (!a->Unicode) {
a->AliasesLength /= sizeof(WCHAR);
}
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
VOID
ClearAliases(
IN PCONSOLE_INFORMATION Console
)
{
PEXE_ALIAS_LIST ExeAliasList;
PLIST_ENTRY ListHead, ListNext;
PALIAS Alias;
ExeAliasList = FindExe(Console,
L"cmd.exe",
14,
TRUE);
if (ExeAliasList == NULL) {
return;
}
ListHead = &ExeAliasList->AliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
ListNext = ListNext->Flink;
RemoveAlias(Alias);
}
}
DWORD
SrvGetConsoleAliases(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_GETALIASES_MSG a = (PCONSOLE_GETALIASES_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
PEXE_ALIAS_LIST ExeAliasList;
PALIAS Alias;
PLIST_ENTRY ListHead, ListNext;
DWORD AliasesBufferLength;
LPWSTR AliasesBufferPtrW;
LPSTR AliasesBufferPtrA;
NTSTATUS Status;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->AliasesBuffer, a->AliasesBufferLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
AliasesBufferLength = a->AliasesBufferLength;
if (a->Unicode) {
AliasesBufferPtrW = a->AliasesBuffer;
} else {
AliasesBufferPtrA = a->AliasesBuffer;
}
a->AliasesBufferLength = 0;
ExeAliasList = FindExe(Console,a->Exe,a->ExeLength,a->UnicodeExe);
if (ExeAliasList) {
ListHead = &ExeAliasList->AliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
Alias = CONTAINING_RECORD( ListNext, ALIAS, ListLink );
if (a->Unicode) {
if ((a->AliasesBufferLength + Alias->SourceLength + Alias->TargetLength + (2*sizeof(WCHAR)))
<= AliasesBufferLength) {
RtlCopyMemory(AliasesBufferPtrW,Alias->Source,Alias->SourceLength);
AliasesBufferPtrW+=Alias->SourceLength/sizeof(WCHAR);
*AliasesBufferPtrW++= (WCHAR)'=';
RtlCopyMemory(AliasesBufferPtrW,Alias->Target,Alias->TargetLength);
AliasesBufferPtrW+=Alias->TargetLength/sizeof(WCHAR);
*AliasesBufferPtrW++= (WCHAR)'\0';
a->AliasesBufferLength += Alias->SourceLength + Alias->TargetLength + (2*sizeof(WCHAR)); // +2 is for = and term null
} else {
UnlockConsole(Console);
return STATUS_BUFFER_OVERFLOW;
}
} else {
if ((a->AliasesBufferLength + ((Alias->SourceLength + Alias->TargetLength)/sizeof(WCHAR)) + (2*sizeof(CHAR)))
<= AliasesBufferLength) {
USHORT SourceLength,TargetLength;
SourceLength = (USHORT)ConvertToOem(Console->CP,
Alias->Source,
Alias->SourceLength / sizeof(WCHAR),
AliasesBufferPtrA,
CHAR_COUNT(Alias->SourceLength)
);
AliasesBufferPtrA+=SourceLength;
*AliasesBufferPtrA++ = '=';
TargetLength = (USHORT)ConvertToOem(Console->CP,
Alias->Target,
Alias->TargetLength / sizeof(WCHAR),
AliasesBufferPtrA,
CHAR_COUNT(Alias->TargetLength)
);
AliasesBufferPtrA+=TargetLength;
*AliasesBufferPtrA++= '\0';
a->AliasesBufferLength += SourceLength + TargetLength + (2*sizeof(CHAR)); // +2 is for = and term null
} else {
UnlockConsole(Console);
return STATUS_BUFFER_OVERFLOW;
}
}
ListNext = ListNext->Flink;
}
}
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
DWORD
SrvGetConsoleAliasExesLength(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_GETALIASEXESLENGTH_MSG a = (PCONSOLE_GETALIASEXESLENGTH_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
PEXE_ALIAS_LIST AliasList;
PLIST_ENTRY ListHead, ListNext;
NTSTATUS Status;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
a->AliasExesLength = 0;
ListHead = &Console->ExeAliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
a->AliasExesLength += AliasList->ExeLength + (1*sizeof(WCHAR)); // +1 for term null
ListNext = ListNext->Flink;
}
if (!a->Unicode) {
a->AliasExesLength /= sizeof(WCHAR);
}
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
DWORD
SrvGetConsoleAliasExes(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_GETALIASEXES_MSG a = (PCONSOLE_GETALIASEXES_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
PEXE_ALIAS_LIST AliasList;
PLIST_ENTRY ListHead, ListNext;
DWORD AliasExesBufferLength;
LPWSTR AliasExesBufferPtrW;
LPSTR AliasExesBufferPtrA;
NTSTATUS Status;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->AliasExesBuffer, a->AliasExesBufferLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
AliasExesBufferLength = a->AliasExesBufferLength;
if (a->Unicode) {
AliasExesBufferPtrW = a->AliasExesBuffer;
} else {
AliasExesBufferPtrA = a->AliasExesBuffer;
}
a->AliasExesBufferLength = 0;
ListHead = &Console->ExeAliasList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
AliasList = CONTAINING_RECORD( ListNext, EXE_ALIAS_LIST, ListLink );
if (a->Unicode) {
if ((a->AliasExesBufferLength + AliasList->ExeLength + (1*sizeof(WCHAR)))
<= AliasExesBufferLength) {
RtlCopyMemory(AliasExesBufferPtrW,AliasList->ExeName,AliasList->ExeLength);
AliasExesBufferPtrW+=AliasList->ExeLength/sizeof(WCHAR);
*AliasExesBufferPtrW++= (WCHAR)'\0';
a->AliasExesBufferLength += AliasList->ExeLength + (1*sizeof(WCHAR)); // +1 is term null
} else {
UnlockConsole(Console);
return STATUS_BUFFER_OVERFLOW;
}
} else {
if ((a->AliasExesBufferLength + (AliasList->ExeLength/sizeof(WCHAR)) + (1*sizeof(CHAR)))
<= AliasExesBufferLength) {
USHORT Length;
Length = (USHORT)ConvertToOem(Console->CP,
AliasList->ExeName,
AliasList->ExeLength / sizeof(WCHAR),
AliasExesBufferPtrA,
CHAR_COUNT(AliasList->ExeLength)
);
AliasExesBufferPtrA+=Length;
*AliasExesBufferPtrA++= (WCHAR)'\0';
a->AliasExesBufferLength += Length + (1*sizeof(CHAR)); // +1 is term null
} else {
UnlockConsole(Console);
return STATUS_BUFFER_OVERFLOW;
}
}
ListNext = ListNext->Flink;
}
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
#define MAX_ARGS 9
NTSTATUS
MatchandCopyAlias(
IN PCONSOLE_INFORMATION Console,
IN PWCHAR Source,
IN USHORT SourceLength,
OUT PWCHAR TargetBuffer,
IN OUT PUSHORT TargetLength,
IN LPWSTR Exe,
IN USHORT ExeLength,
OUT PDWORD LineCount
)
/*++
Routine Description:
This routine matches the input string with an alias and copies the
alias to the input buffer.
Arguments:
Source - string to match
SourceLength - length of Source in bytes
TargetBuffer - where to store matched string
TargetLength - on input, contains size of TargetBuffer. On output,
contains length of alias stored in TargetBuffer.
SourceIsCommandLine - if true, source buffer is a command line, where
the first blank separate token is to be check for an alias, and if
it matches, replaced with the value of the alias. if false, then
the source string is a null terminated alias name.
LineCount - aliases can contain multiple commands. $T is the command
separator
Return Value:
SUCCESS - match was found and alias was copied to buffer.
--*/
{
PALIAS Alias;
NTSTATUS Status = STATUS_SUCCESS;
USHORT SourceUpToFirstBlank=0; // in chars
PWCHAR Tmp;
PEXE_ALIAS_LIST ExeAliasList;
LPWSTR Args[MAX_ARGS];
USHORT ArgsLength[MAX_ARGS]; // in bytes
USHORT NumSourceArgs;
LPWSTR SourcePtr;
USHORT ArgCount,i,j,NewTargetLength;
USHORT SourceRemainderLength; // in chars
PWCHAR Buffer,TargetAlias;
PWCHAR TmpBuffer;
//
// alloc of exename may have failed.
//
if (Exe == NULL)
return STATUS_UNSUCCESSFUL;
//
// find exe
//
ExeAliasList = FindExe(Console,Exe,ExeLength,TRUE);
if (!ExeAliasList) {
return STATUS_UNSUCCESSFUL;
}
//
// find first blank
//
for (Tmp=Source,SourceUpToFirstBlank=0;
*Tmp!=(WCHAR)' ' && SourceUpToFirstBlank<(USHORT)(SourceLength/sizeof(WCHAR));
Tmp++,SourceUpToFirstBlank++) ;
//
// find char past first blank
//
j=SourceUpToFirstBlank;
while (j<(USHORT)(SourceLength/sizeof(WCHAR)) && *Tmp==(WCHAR)' ') {
Tmp++;
j++;
}
SourcePtr = Tmp;
SourceRemainderLength = (USHORT)((SourceLength/sizeof(WCHAR)) - j);
//
// find alias
//
Alias = FindAlias(ExeAliasList,Source,(USHORT)(SourceUpToFirstBlank*sizeof(WCHAR)));
if (!Alias) {
return STATUS_UNSUCCESSFUL;
}
TmpBuffer = ConsoleHeapAlloc(TMP_TAG, *TargetLength);
if (!TmpBuffer) {
return STATUS_NO_MEMORY;
}
//
// count args in target
//
ArgCount=0;
*LineCount=1;
Tmp=Alias->Target;
for (i=0;(USHORT)(i+1)<(USHORT)(Alias->TargetLength/sizeof(WCHAR));i++) {
if (*Tmp == (WCHAR)'$' && *(Tmp+1) >= (WCHAR)'1' && *(Tmp+1) <= (WCHAR)'9') {
USHORT ArgNum = *(Tmp+1) - (WCHAR)'0';
if (ArgNum > ArgCount) {
ArgCount = ArgNum;
}
Tmp++;
i++;
} else if (*Tmp == (WCHAR)'$' && *(Tmp+1) == (WCHAR)'*') {
if (ArgCount==0) {
ArgCount = 1;
}
Tmp++;
i++;
}
Tmp++;
}
//
// package up space separated strings in source into array
// of args
//
//
NumSourceArgs=0;
Tmp = SourcePtr;
for (i=0,j=0;i<ArgCount;i++) {
if (j<SourceRemainderLength) {
Args[NumSourceArgs] = Tmp;
ArgsLength[NumSourceArgs] = 0;
while (j++<SourceRemainderLength && *Tmp++ != (WCHAR)' ') {
ArgsLength[NumSourceArgs] += sizeof(WCHAR);
}
while (j<SourceRemainderLength && *Tmp == (WCHAR)' ') {
j++;
Tmp++;
}
NumSourceArgs++;
} else {
break;
}
}
//
// put together the target string
//
// while (target)
// if ($)
// if arg && arg# <= ArgCount
// copy arg
// else if *
// copy arg
// else
// replace with < > etc
// else
// copy text up to next ' '
//
Buffer = TmpBuffer;
NewTargetLength = 2*sizeof(WCHAR); // for CRLF
TargetAlias=Alias->Target;
for (i=0;i<(USHORT)(Alias->TargetLength/sizeof(WCHAR));i++) {
if (NewTargetLength >= *TargetLength) {
*TargetLength = NewTargetLength;
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
if (*TargetAlias == (WCHAR)'$' && (USHORT)(i+1)<(USHORT)(Alias->TargetLength/sizeof(WCHAR))) {
TargetAlias++;
i++;
if (*TargetAlias >= (WCHAR)'1' && *TargetAlias <= (WCHAR)'9') {
//
// do numbered parameter substitution
//
USHORT ArgNumber;
ArgNumber = (USHORT)(*TargetAlias - (WCHAR)'1');
if (ArgNumber < NumSourceArgs) {
if ((NewTargetLength+ArgsLength[ArgNumber])<=*TargetLength) {
RtlCopyMemory(Buffer,Args[ArgNumber],ArgsLength[ArgNumber]);
Buffer+=ArgsLength[ArgNumber]/sizeof(WCHAR);
NewTargetLength+=ArgsLength[ArgNumber];
} else {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
}
} else if (*TargetAlias == (WCHAR)'*') {
//
// do * parameter substitution
//
if (NumSourceArgs) {
if ((USHORT)(NewTargetLength+(SourceRemainderLength*sizeof(WCHAR)))<=*TargetLength) {
RtlCopyMemory(Buffer,Args[0],SourceRemainderLength*sizeof(WCHAR));
Buffer+=SourceRemainderLength;
NewTargetLength+=SourceRemainderLength*sizeof(WCHAR);
} else {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
}
} else if (*TargetAlias == (WCHAR)'l' || *TargetAlias == (WCHAR)'L') {
//
// do < substitution
//
*Buffer++ = (WCHAR)'<';
NewTargetLength+=sizeof(WCHAR);
} else if (*TargetAlias == (WCHAR)'g' || *TargetAlias == (WCHAR)'G') {
//
// do > substitution
//
*Buffer++ = (WCHAR)'>';
NewTargetLength+=sizeof(WCHAR);
} else if (*TargetAlias == (WCHAR)'b' || *TargetAlias == (WCHAR)'B') {
//
// do | substitution
//
*Buffer++ = (WCHAR)'|';
NewTargetLength+=sizeof(WCHAR);
} else if (*TargetAlias == (WCHAR)'t' || *TargetAlias == (WCHAR)'T') {
//
// do newline substitution
//
if ((USHORT)(NewTargetLength+(sizeof(WCHAR)*2))>*TargetLength) {
Status = STATUS_BUFFER_TOO_SMALL;
}
*LineCount += 1;
*Buffer++ = UNICODE_CARRIAGERETURN;
*Buffer++ = UNICODE_LINEFEED;
NewTargetLength+=sizeof(WCHAR)*2;
} else {
//
// copy $X
//
*Buffer++ = (WCHAR)'$';
NewTargetLength+=sizeof(WCHAR);
*Buffer++ = *TargetAlias;
NewTargetLength+=sizeof(WCHAR);
}
TargetAlias++;
} else {
//
// copy char
//
*Buffer++ = *TargetAlias++;
NewTargetLength+=sizeof(WCHAR);
}
}
*Buffer++ = UNICODE_CARRIAGERETURN;
*Buffer++ = UNICODE_LINEFEED;
RtlCopyMemory(TargetBuffer,TmpBuffer,NewTargetLength);
ConsoleHeapFree(TmpBuffer);
*TargetLength = NewTargetLength;
return Status;
}
DWORD
SrvExpungeConsoleCommandHistory(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_EXPUNGECOMMANDHISTORY_MSG a = (PCONSOLE_EXPUNGECOMMANDHISTORY_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
NTSTATUS Status;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
EmptyCommandHistory(FindExeCommandHistory(Console,
a->Exe,
a->ExeLength,
a->UnicodeExe));
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
DWORD
SrvSetConsoleNumberOfCommands(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_SETNUMBEROFCOMMANDS_MSG a = (PCONSOLE_SETNUMBEROFCOMMANDS_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
NTSTATUS Status;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
ReallocCommandHistory(Console,
FindExeCommandHistory(Console,
a->Exe,
a->ExeLength,
a->UnicodeExe),
a->NumCommands
);
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
DWORD
SrvGetConsoleCommandHistoryLength(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_GETCOMMANDHISTORYLENGTH_MSG a = (PCONSOLE_GETCOMMANDHISTORYLENGTH_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
NTSTATUS Status;
SHORT i;
PCOMMAND_HISTORY CommandHistory;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
a->CommandHistoryLength=0;
CommandHistory=FindExeCommandHistory(Console,
a->Exe,
a->ExeLength,
a->UnicodeExe);
if (CommandHistory) {
for (i=0;i<CommandHistory->NumberOfCommands;i++) {
a->CommandHistoryLength+=CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR);
}
}
if (!a->Unicode) {
a->CommandHistoryLength /= sizeof(WCHAR);
}
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
DWORD
SrvGetConsoleCommandHistory(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_GETCOMMANDHISTORY_MSG a = (PCONSOLE_GETCOMMANDHISTORY_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
NTSTATUS Status;
SHORT i,CommandHistoryLength;
PCOMMAND_HISTORY CommandHistory;
PWCHAR CommandBufferW;
PCHAR CommandBufferA;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->CommandBuffer, a->CommandBufferLength, sizeof(BYTE)) ||
!CsrValidateMessageBuffer(m, &a->Exe, a->ExeLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
if (a->Unicode) {
CommandBufferW=a->CommandBuffer;
} else {
CommandBufferA=a->CommandBuffer;
}
CommandHistoryLength=0;
CommandHistory=FindExeCommandHistory(Console,
a->Exe,
a->ExeLength,
a->UnicodeExe);
if (CommandHistory) {
for (i=0;i<CommandHistory->NumberOfCommands;i++) {
if (a->Unicode) {
if ((CommandHistoryLength+CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR)) <= a->CommandBufferLength) {
RtlCopyMemory(CommandBufferW,CommandHistory->Commands[i]->Command,CommandHistory->Commands[i]->CommandLength);
CommandBufferW+=CommandHistory->Commands[i]->CommandLength/sizeof(WCHAR);
*CommandBufferW++=(WCHAR)'\0';
CommandHistoryLength+=CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR);
} else {
Status = STATUS_BUFFER_OVERFLOW;
break;
}
} else {
if ((CommandHistoryLength+(CommandHistory->Commands[i]->CommandLength/sizeof(WCHAR))+sizeof(CHAR)) <= a->CommandBufferLength) {
USHORT Length;
Length = (USHORT)ConvertToOem(Console->CP,
CommandHistory->Commands[i]->Command,
CommandHistory->Commands[i]->CommandLength / sizeof(WCHAR),
CommandBufferA,
CHAR_COUNT(CommandHistory->Commands[i]->CommandLength)
);
CommandBufferA+=Length;
*CommandBufferA++=(WCHAR)'\0';
CommandHistoryLength+=CommandHistory->Commands[i]->CommandLength+sizeof(WCHAR);
} else {
Status = STATUS_BUFFER_OVERFLOW;
break;
}
}
}
}
a->CommandBufferLength=CommandHistoryLength;
UnlockConsole(Console);
return Status;
UNREFERENCED_PARAMETER(ReplyStatus);
}
DWORD
SrvSetConsoleCommandHistoryMode(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_SETCOMMANDHISTORYMODE_MSG a = (PCONSOLE_SETCOMMANDHISTORYMODE_MSG)&m->u.ApiMessageData;
PCONSOLE_INFORMATION Console;
NTSTATUS Status;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
Console->InsertMode = (BOOLEAN) (a->Flags != CONSOLE_OVERSTRIKE);
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus);
}
PCOMMAND_HISTORY
ReallocCommandHistory(
IN PCONSOLE_INFORMATION Console,
IN PCOMMAND_HISTORY CurrentCommandHistory,
IN DWORD NumCommands)
{
PCOMMAND_HISTORY History;
int i;
/*
* To protect ourselves from overflow, a limit of 0xFFFF is put on the
* size of the command history.
*/
if (CurrentCommandHistory == NULL ||
CurrentCommandHistory->MaximumNumberOfCommands == (SHORT)NumCommands ||
NumCommands > 0xFFFF) {
return CurrentCommandHistory;
}
History = ConsoleHeapAlloc(HISTORY_TAG,
sizeof(COMMAND_HISTORY) + NumCommands * sizeof(PCOMMAND));
if (History == NULL) {
return CurrentCommandHistory;
}
*History = *CurrentCommandHistory;
History->Flags |= CLE_RESET;
History->NumberOfCommands = min(History->NumberOfCommands, (SHORT)NumCommands);
History->LastAdded = History->NumberOfCommands - 1;
History->LastDisplayed = History->NumberOfCommands - 1;
History->FirstCommand = 0;
History->MaximumNumberOfCommands = (SHORT)NumCommands;
for (i = 0; i < History->NumberOfCommands; i++) {
History->Commands[i] = CurrentCommandHistory->Commands[COMMAND_NUM_TO_INDEX(i, CurrentCommandHistory)];
}
for (; i < CurrentCommandHistory->NumberOfCommands; i++) {
ConsoleHeapFree(CurrentCommandHistory->Commands[COMMAND_NUM_TO_INDEX(i, CurrentCommandHistory)]);
}
RemoveEntryList(&CurrentCommandHistory->ListLink);
InitializeListHead(&History->PopupList);
InsertHeadList(&Console->CommandHistoryList,&History->ListLink);
ConsoleHeapFree(CurrentCommandHistory);
return History;
}
PCOMMAND_HISTORY
FindExeCommandHistory(
IN PCONSOLE_INFORMATION Console,
IN PVOID AppName,
IN DWORD AppNameLength,
IN BOOLEAN Unicode
)
{
PCOMMAND_HISTORY History;
PLIST_ENTRY ListHead, ListNext;
PWCHAR AppNamePtr;
if (!Unicode) {
AppNamePtr = ConsoleHeapAlloc(TMP_TAG, AppNameLength * sizeof(WCHAR));
if (AppNamePtr == NULL) {
return NULL;
}
AppNameLength = ConvertInputToUnicode(Console->CP,
AppName,
AppNameLength,
AppNamePtr,
AppNameLength);
AppNameLength *= 2;
} else {
AppNamePtr = AppName;
}
ListHead = &Console->CommandHistoryList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
ListNext = ListNext->Flink;
if (History->Flags & CLE_ALLOCATED &&
!my_wcsncmpi(History->AppName,AppNamePtr,(USHORT)AppNameLength)) {
if (!Unicode) {
ConsoleHeapFree(AppNamePtr);
}
return History;
}
}
if (!Unicode) {
ConsoleHeapFree(AppNamePtr);
}
return NULL;
}
PCOMMAND_HISTORY
AllocateCommandHistory(
IN PCONSOLE_INFORMATION Console,
IN DWORD AppNameLength,
IN PWCHAR AppName,
IN HANDLE ProcessHandle
)
/*++
Routine Description:
This routine returns the LRU command history buffer, or the command history
buffer that corresponds to the app name.
Arguments:
Console - pointer to console.
Return Value:
Pointer to command history buffer. if none are available, returns NULL.
--*/
{
PCOMMAND_HISTORY History,BestCandidate;
PLIST_ENTRY ListHead, ListNext;
BOOL SameApp;
//
// Reuse a history buffer. The buffer must be !CLE_ALLOCATED.
// If possible, the buffer should have the same app name.
//
ListHead = &Console->CommandHistoryList;
ListNext = ListHead->Blink;
BestCandidate = NULL;
SameApp = FALSE;
while (ListNext != ListHead) {
History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
ListNext = ListNext->Blink;
if ((History->Flags & CLE_ALLOCATED) == 0) {
//
// use LRU history buffer with same app name
//
if (History->AppName && !my_wcsncmpi(History->AppName,AppName,(USHORT)AppNameLength)) {
BestCandidate = History;
SameApp = TRUE;
break;
}
//
// second best choice is LRU history buffer
//
if (BestCandidate == NULL) {
BestCandidate = History;
}
}
}
//
// if there isn't a free buffer for the app name and the maximum number of
// command history buffers hasn't been allocated, allocate a new one.
//
if (!SameApp && Console->NumCommandHistories < Console->MaxCommandHistories) {
History = ConsoleHeapAlloc(HISTORY_TAG,
sizeof(COMMAND_HISTORY) + Console->CommandHistorySize * sizeof(PCOMMAND));
if (History == NULL) {
return NULL;
}
History->AppName = ConsoleHeapAlloc(HISTORY_TAG, AppNameLength);
if (History->AppName == NULL) {
ConsoleHeapFree(History);
return NULL;
}
RtlCopyMemory(History->AppName,AppName,AppNameLength);
History->Flags = CLE_ALLOCATED;
History->NumberOfCommands = 0;
History->LastAdded = -1;
History->LastDisplayed = -1;
History->FirstCommand = 0;
History->MaximumNumberOfCommands = Console->CommandHistorySize;
InsertHeadList(&Console->CommandHistoryList,&History->ListLink);
Console->NumCommandHistories+=1;
History->ProcessHandle = ProcessHandle;
InitializeListHead(&History->PopupList);
return History;
}
//
// if the app name doesn't match, copy in the new app name and free the old commands.
//
if (BestCandidate) {
History = BestCandidate;
UserAssert(CLE_NO_POPUPS(History));
if (!SameApp) {
SHORT i;
if (History->AppName) {
DBGPRINT(("Reusing %ls command history\n", History->AppName));
ConsoleHeapFree(History->AppName);
}
for (i=0;i<History->NumberOfCommands;i++) {
ConsoleHeapFree(History->Commands[i]);
}
History->NumberOfCommands = 0;
History->LastAdded = -1;
History->LastDisplayed = -1;
History->FirstCommand = 0;
History->AppName = ConsoleHeapAlloc(HISTORY_TAG, AppNameLength);
if (History->AppName == NULL) {
History->Flags &= ~CLE_ALLOCATED;
return NULL;
}
RtlCopyMemory(History->AppName,AppName,AppNameLength);
}
History->ProcessHandle = ProcessHandle;
History->Flags |= CLE_ALLOCATED;
//
// move to the front of the list
//
RemoveEntryList(&BestCandidate->ListLink);
InsertHeadList(&Console->CommandHistoryList,&BestCandidate->ListLink);
}
return BestCandidate;
}
NTSTATUS
BeginPopup(
IN PSCREEN_INFORMATION ScreenInfo,
IN PCOMMAND_HISTORY CommandHistory,
IN COORD PopupSize
)
{
COORD Origin;
COORD Size;
PCLE_POPUP Popup;
SMALL_RECT TargetRect;
// determine popup dimensions
Size = PopupSize;
Size.X += 2; // add borders
Size.Y += 2; // add borders
if (Size.X >= (SHORT)(CONSOLE_WINDOW_SIZE_X(ScreenInfo))) {
Size.X = (SHORT)(CONSOLE_WINDOW_SIZE_X(ScreenInfo));
}
if (Size.Y >= (SHORT)(CONSOLE_WINDOW_SIZE_Y(ScreenInfo))) {
Size.Y = (SHORT)(CONSOLE_WINDOW_SIZE_Y(ScreenInfo));
}
// make sure there's enough room for the popup borders
if (Size.X < 2 || Size.Y < 2) {
return STATUS_BUFFER_TOO_SMALL;
}
// determine origin. center popup on window
Origin.X = (SHORT)((CONSOLE_WINDOW_SIZE_X(ScreenInfo) - Size.X) / 2 + ScreenInfo->Window.Left);
Origin.Y = (SHORT)((CONSOLE_WINDOW_SIZE_Y(ScreenInfo) - Size.Y) / 2 + ScreenInfo->Window.Top);
// allocate a popup structure
Popup = ConsoleHeapAlloc(TMP_TAG, sizeof(CLE_POPUP));
if (Popup == NULL) {
return STATUS_NO_MEMORY;
}
// allocate a buffer
#if !defined(FE_SB)
Popup->OldContents = ConsoleHeapAlloc(TMP_TAG, Size.X * Size.Y * sizeof(CHAR_INFO));
#else
Popup->OldScreenSize = ScreenInfo->ScreenBufferSize;
Popup->OldContents = ConsoleHeapAlloc(TMP_TAG, Popup->OldScreenSize.X * Size.Y * sizeof(CHAR_INFO));
#endif
if (Popup->OldContents == NULL) {
ConsoleHeapFree(Popup);
return STATUS_NO_MEMORY;
}
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
Popup->Flags |= CLEPF_FALSE_UNICODE;
} else {
Popup->Flags &= ~CLEPF_FALSE_UNICODE;
}
//
// fill in popup structure
//
InsertHeadList(&CommandHistory->PopupList,&Popup->ListLink);
Popup->Region.Left = Origin.X;
Popup->Region.Top = Origin.Y;
Popup->Region.Right = (SHORT)(Origin.X + Size.X - 1);
Popup->Region.Bottom = (SHORT)(Origin.Y + Size.Y - 1);
Popup->Attributes = ScreenInfo->PopupAttributes;
Popup->BottomIndex = COMMAND_INDEX_TO_NUM(CommandHistory->LastDisplayed,CommandHistory);
//
// copy old contents
//
#if !defined(FE_SB)
TargetRect = Popup->Region;
#else
TargetRect.Left = 0;
TargetRect.Top = Popup->Region.Top;
TargetRect.Right = Popup->OldScreenSize.X - 1;
TargetRect.Bottom = Popup->Region.Bottom;
#endif
ReadScreenBuffer(ScreenInfo,
Popup->OldContents,
&TargetRect);
ScreenInfo->Console->PopupCount++;
DrawCommandListBorder(Popup,ScreenInfo);
return STATUS_SUCCESS;
}
NTSTATUS
EndPopup(
IN PSCREEN_INFORMATION ScreenInfo,
IN PCOMMAND_HISTORY CommandHistory)
{
COORD Size;
SMALL_RECT SourceRect;
PCLE_POPUP Popup;
UserAssert(!CLE_NO_POPUPS(CommandHistory));
if (CLE_NO_POPUPS(CommandHistory)) {
return STATUS_UNSUCCESSFUL;
}
ConsoleHideCursor(ScreenInfo);
Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
//
// restore previous contents to screen
//
#if !defined(FE_SB)
Size.X = (SHORT)(Popup->Region.Right - Popup->Region.Left + 1);
Size.Y = (SHORT)(Popup->Region.Bottom - Popup->Region.Top + 1);
SourceRect = Popup->Region;
#else
Size.X = Popup->OldScreenSize.X;
Size.Y = (SHORT)(Popup->Region.Bottom - Popup->Region.Top + 1);
SourceRect.Left = 0;
SourceRect.Top = Popup->Region.Top;
SourceRect.Right = Popup->OldScreenSize.X - 1;
SourceRect.Bottom = Popup->Region.Bottom;
#endif
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
/*
* Screen buffer wants fake Unicode
*/
if (!(Popup->Flags & CLEPF_FALSE_UNICODE)) {
#if !defined(FE_SB)
TranslateOutputToAnsiUnicode(ScreenInfo->Console,
Popup->OldContents, Size);
#else
TranslateOutputToAnsiUnicode(ScreenInfo->Console,
Popup->OldContents, Size,
NULL);
#endif
}
} else {
/*
* Screen buffer wants real Unicode
*/
if (Popup->Flags & CLEPF_FALSE_UNICODE) {
#if !defined(FE_SB)
TranslateOutputToOemUnicode(ScreenInfo->Console,
Popup->OldContents, Size);
#else
TranslateOutputToOemUnicode(ScreenInfo->Console,
Popup->OldContents, Size, FALSE);
#endif
}
}
WriteScreenBuffer(ScreenInfo,
Popup->OldContents,
&SourceRect
);
WriteToScreen(ScreenInfo,
&SourceRect
);
ConsoleShowCursor(ScreenInfo);
//
// free popup structure
//
RemoveEntryList(&Popup->ListLink);
ConsoleHeapFree(Popup->OldContents);
ConsoleHeapFree(Popup);
ScreenInfo->Console->PopupCount--;
return STATUS_SUCCESS;
}
VOID
CleanUpPopups(
IN PCOOKED_READ_DATA CookedReadData
)
{
PCOMMAND_HISTORY CommandHistory;
CommandHistory = CookedReadData->CommandHistory;
if (!CommandHistory)
return;
while (!CLE_NO_POPUPS(CommandHistory)) {
EndPopup(CookedReadData->ScreenInfo,CommandHistory);
}
}
VOID
DeleteCommandLine(
IN OUT PCOOKED_READ_DATA CookedReadData,
IN BOOL UpdateFields
)
{
DWORD CharsToWrite = CookedReadData->NumberOfVisibleChars;
COORD Coord = CookedReadData->OriginalCursorPosition;
//
// catch the case where the current command has scrolled off the
// top of the screen.
//
if (Coord.Y < 0) {
CharsToWrite += CookedReadData->ScreenInfo->ScreenBufferSize.X * Coord.Y;
CharsToWrite += CookedReadData->OriginalCursorPosition.X; // account for prompt
CookedReadData->OriginalCursorPosition.X = 0;
CookedReadData->OriginalCursorPosition.Y = 0;
Coord.X = 0;
Coord.Y = 0;
}
#if defined(FE_SB)
if (CONSOLE_IS_DBCS_OUTPUTCP(CookedReadData->ScreenInfo->Console) &&
!CheckBisectStringW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CharsToWrite,
CookedReadData->ScreenInfo->ScreenBufferSize.X
-CookedReadData->OriginalCursorPosition.X
)) {
CharsToWrite++;
}
#endif
FillOutput(CookedReadData->ScreenInfo,
(WCHAR)' ',
Coord,
CONSOLE_FALSE_UNICODE, // faster than real unicode
&CharsToWrite
);
if (UpdateFields) {
CookedReadData->BufPtr=CookedReadData->BackupLimit;
CookedReadData->BytesRead=0;
CookedReadData->CurrentPosition=0;
CookedReadData->NumberOfVisibleChars = 0;
}
SetCursorPosition(CookedReadData->ScreenInfo,
CookedReadData->OriginalCursorPosition,
TRUE
);
}
VOID
RedrawCommandLine(
IN OUT PCOOKED_READ_DATA CookedReadData
)
{
NTSTATUS Status;
COORD CursorPosition;
SHORT ScrollY=0;
if (CookedReadData->Echo) {
//
// Draw the command line
//
CookedReadData->OriginalCursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
&CookedReadData->BytesRead,
&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
//
// Move the cursor back to the right position
//
CursorPosition = CookedReadData->OriginalCursorPosition;
CursorPosition.X += (SHORT)RetrieveTotalNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition,
CookedReadData->ScreenInfo->Console);
if (CheckBisectStringW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition,
CookedReadData->ScreenInfo->ScreenBufferSize.X
-CookedReadData->OriginalCursorPosition.X)) {
CursorPosition.X++;
}
Status = AdjustCursorPosition(CookedReadData->ScreenInfo,
CursorPosition,
TRUE,
NULL);
UserAssert(NT_SUCCESS(Status));
}
}
NTSTATUS
RetrieveNthCommand(
IN PCOMMAND_HISTORY CommandHistory,
IN SHORT Index, // index, not command number
IN PWCHAR Buffer,
IN ULONG BufferSize,
OUT PULONG CommandSize)
{
PCOMMAND CommandRecord;
UserAssert(Index < CommandHistory->NumberOfCommands);
CommandHistory->LastDisplayed = Index;
CommandRecord = CommandHistory->Commands[Index];
if (CommandRecord->CommandLength > (USHORT)BufferSize) {
*CommandSize = (USHORT)BufferSize; // room for CRLF?
} else {
*CommandSize = CommandRecord->CommandLength;
}
RtlCopyMemory(Buffer,CommandRecord->Command,*CommandSize);
return STATUS_SUCCESS;
}
VOID
SetCurrentCommandLine(
IN PCOOKED_READ_DATA CookedReadData,
IN SHORT Index // index, not command number
)
/*++
This routine copies the commandline specified by Index
into the cooked read buffer
--*/
{
DWORD CharsToWrite;
NTSTATUS Status;
SHORT ScrollY=0;
DeleteCommandLine(CookedReadData,
TRUE);
Status = RetrieveNthCommand(CookedReadData->CommandHistory,
Index,
CookedReadData->BackupLimit,
CookedReadData->BufferSize,
&CookedReadData->BytesRead);
UserAssert(NT_SUCCESS(Status));
UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
}
CharsToWrite = CookedReadData->BytesRead/sizeof(WCHAR);
CookedReadData->CurrentPosition = CharsToWrite;
CookedReadData->BufPtr = CookedReadData->BackupLimit + CharsToWrite;
}
BOOL
IsCommandLinePopupKey(
IN OUT PKEY_EVENT_RECORD KeyEvent
)
{
if (!(KeyEvent->dwControlKeyState &
(RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED |
RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))) {
switch (KeyEvent->wVirtualKeyCode) {
case VK_ESCAPE:
case VK_PRIOR:
case VK_NEXT:
case VK_END:
case VK_HOME:
case VK_LEFT:
case VK_UP:
case VK_RIGHT:
case VK_DOWN:
case VK_F9:
return TRUE;
default:
break;
}
}
//
// Extended key handling
//
if (gExtendedEditKey && ParseEditKeyInfo(KeyEvent)) {
return KeyEvent->uChar.UnicodeChar == 0;
}
return FALSE;
}
BOOL
IsCommandLineEditingKey(
IN PKEY_EVENT_RECORD KeyEvent
)
{
if (!(KeyEvent->dwControlKeyState &
(RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED |
RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))) {
switch (KeyEvent->wVirtualKeyCode) {
case VK_ESCAPE:
case VK_PRIOR:
case VK_NEXT:
case VK_END:
case VK_HOME:
case VK_LEFT:
case VK_UP:
case VK_RIGHT:
case VK_DOWN:
case VK_INSERT:
case VK_DELETE:
case VK_F1:
case VK_F2:
case VK_F3:
case VK_F4:
case VK_F5:
case VK_F6:
case VK_F7:
case VK_F8:
case VK_F9:
return TRUE;
default:
break;
}
}
if ((KeyEvent->dwControlKeyState &
(RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))) {
switch (KeyEvent->wVirtualKeyCode) {
case VK_END:
case VK_HOME:
case VK_LEFT:
case VK_RIGHT:
return TRUE;
default:
break;
}
}
//
// Extended edit key handling
//
if (gExtendedEditKey && ParseEditKeyInfo(KeyEvent)) {
//
// If wUnicodeChar is specified in KeySubst,
// the key should be handled as a normal key.
// Basically this is for VK_BACK keys.
//
return KeyEvent->uChar.UnicodeChar == 0;
}
if ((KeyEvent->dwControlKeyState &
(RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))) {
switch (KeyEvent->wVirtualKeyCode) {
case VK_F7:
case VK_F10:
return TRUE;
default:
break;
}
}
return FALSE;
}
NTSTATUS
ProcessCommandListInput(
IN PVOID CookedReadDataPtr,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the command list popup. It returns
when we're out of input or the user has selected a command line.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
CONSOLE_STATUS_READ_COMPLETE - user hit return
--*/
{
NTSTATUS Status;
PCLE_POPUP Popup;
PCOMMAND_HISTORY CommandHistory;
PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
WCHAR Char;
BOOLEAN CommandLinePopupKeys = FALSE;
PCONSOLE_READCONSOLE_MSG a;
PHANDLE_DATA HandleData;
SHORT Index;
CommandHistory = CookedReadData->CommandHistory;
Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
CookedReadData->HandleIndex,
&HandleData
);
UserAssert(NT_SUCCESS(Status));
while (TRUE) {
Status = GetChar(CookedReadData->InputInfo,
&Char,
TRUE,
CookedReadData->Console,
HandleData,
WaitReplyMessage,
CookedReadWaitRoutine,
CookedReadData,
sizeof(*CookedReadData),
WaitRoutine,
NULL,
&CommandLinePopupKeys,
NULL,
NULL
);
if (!NT_SUCCESS(Status)) {
if (Status != CONSOLE_STATUS_WAIT) {
CookedReadData->BytesRead = 0;
}
return Status;
}
if (CommandLinePopupKeys) {
switch (Char) {
case VK_F9:
//
// prompt the user to enter the desired command number.
// copy that command to the command line.
//
{
COORD PopupSize;
if (CookedReadData->CommandHistory &&
CookedReadData->ScreenInfo->ScreenBufferSize.X >= MINIMUM_COMMAND_PROMPT_SIZE+2) { // 2 is for border
PopupSize.X = COMMAND_NUMBER_PROMPT_LENGTH+COMMAND_NUMBER_LENGTH;
PopupSize.Y = 1;
Status = BeginPopup(CookedReadData->ScreenInfo,
CookedReadData->CommandHistory,
PopupSize
);
if (NT_SUCCESS(Status)) {
// CommandNumberPopup does EndPopup call
return CommandNumberPopup(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine
);
}
}
}
break;
case VK_ESCAPE:
EndPopup(CookedReadData->ScreenInfo,CommandHistory);
HandleData->InputReadData->ReadCount += 1;
return CONSOLE_STATUS_WAIT_NO_BLOCK;
case VK_UP:
UpdateCommandListPopup(-1,
&Popup->CurrentCommand,
CommandHistory,
Popup,
CookedReadData->ScreenInfo, 0);
break;
case VK_DOWN:
UpdateCommandListPopup(1,
&Popup->CurrentCommand,
CommandHistory,
Popup,
CookedReadData->ScreenInfo, 0);
break;
case VK_END:
/*
* Move waaay forward, UpdateCommandListPopup() can handle it.
*/
UpdateCommandListPopup((SHORT)(CommandHistory->NumberOfCommands),
&Popup->CurrentCommand,
CommandHistory,
Popup,
CookedReadData->ScreenInfo, 0);
break;
case VK_HOME:
/*
* Move waaay back, UpdateCommandListPopup() can handle it.
*/
UpdateCommandListPopup((SHORT)-(CommandHistory->NumberOfCommands),
&Popup->CurrentCommand,
CommandHistory,
Popup,
CookedReadData->ScreenInfo, 0);
break;
case VK_PRIOR:
UpdateCommandListPopup((SHORT)-POPUP_SIZE_Y(Popup),
&Popup->CurrentCommand,
CommandHistory,
Popup,
CookedReadData->ScreenInfo, 0);
break;
case VK_NEXT:
UpdateCommandListPopup(POPUP_SIZE_Y(Popup),
&Popup->CurrentCommand,
CommandHistory,
Popup,
CookedReadData->ScreenInfo, 0);
break;
case VK_LEFT:
case VK_RIGHT:
Index = Popup->CurrentCommand;
EndPopup(CookedReadData->ScreenInfo,CommandHistory);
SetCurrentCommandLine(CookedReadData,Index);
HandleData->InputReadData->ReadCount += 1;
return CONSOLE_STATUS_WAIT_NO_BLOCK;
default:
break;
}
} else if (Char == UNICODE_CARRIAGERETURN) {
ULONG i,lStringLength;
DWORD LineCount=1;
Index = Popup->CurrentCommand;
EndPopup(CookedReadData->ScreenInfo,CommandHistory);
SetCurrentCommandLine(CookedReadData,Index);
lStringLength = CookedReadData->BytesRead;
ProcessCookedReadInput(CookedReadData,
UNICODE_CARRIAGERETURN,
0,
&Status);
//
// complete read
//
if (CookedReadData->Echo) {
//
// check for alias
//
i = CookedReadData->BufferSize;
if (NT_SUCCESS(MatchandCopyAlias(CookedReadData->Console,
CookedReadData->BackupLimit,
(USHORT)lStringLength,
CookedReadData->BackupLimit,
(PUSHORT)&i,
CookedReadData->ExeName,
CookedReadData->ExeNameLength,
&LineCount
))) {
CookedReadData->BytesRead = i;
}
CloseOutputHandle(CONSOLE_FROMTHREADPERPROCESSDATA(WaitingThread),
CookedReadData->Console,
&CookedReadData->TempHandle,
NULL,
FALSE
);
}
WaitReplyMessage->ReturnValue = STATUS_SUCCESS;
a = (PCONSOLE_READCONSOLE_MSG)&WaitReplyMessage->u.ApiMessageData;
if (CookedReadData->BytesRead > CookedReadData->UserBufferSize || LineCount > 1) {
if (LineCount > 1) {
PWSTR Tmp;
HandleData->InputReadData->InputHandleFlags |= HANDLE_MULTI_LINE_INPUT;
for (Tmp=CookedReadData->BackupLimit;*Tmp!=UNICODE_LINEFEED;Tmp++)
UserAssert(Tmp<(CookedReadData->BackupLimit+CookedReadData->BytesRead));
a->NumBytes = (ULONG)(Tmp-CookedReadData->BackupLimit+1)*sizeof(*Tmp);
} else {
a->NumBytes = CookedReadData->UserBufferSize;
}
HandleData->InputReadData->InputHandleFlags |= HANDLE_INPUT_PENDING;
HandleData->InputReadData->BufPtr = CookedReadData->BackupLimit;
HandleData->InputReadData->BytesAvailable = CookedReadData->BytesRead - a->NumBytes;
HandleData->InputReadData->CurrentBufPtr=(PWCHAR)((PBYTE)CookedReadData->BackupLimit+a->NumBytes);
RtlCopyMemory(CookedReadData->UserBuffer,CookedReadData->BackupLimit,a->NumBytes);
} else {
a->NumBytes = CookedReadData->BytesRead;
RtlCopyMemory(CookedReadData->UserBuffer,CookedReadData->BackupLimit,a->NumBytes);
}
if (!a->Unicode) {
//
// if ansi, translate string.
//
PCHAR TransBuffer;
TransBuffer = ConsoleHeapAlloc(TMP_TAG, CHAR_COUNT(a->NumBytes));
if (TransBuffer == NULL) {
return STATUS_NO_MEMORY;
}
a->NumBytes = (ULONG)ConvertToOem(CookedReadData->Console->CP,
CookedReadData->UserBuffer,
a->NumBytes / sizeof(WCHAR),
TransBuffer,
CHAR_COUNT(a->NumBytes)
);
RtlCopyMemory(CookedReadData->UserBuffer,TransBuffer,a->NumBytes);
ConsoleHeapFree(TransBuffer);
}
return CONSOLE_STATUS_READ_COMPLETE;
} else {
Index = FindMatchingCommand(CookedReadData->CommandHistory,
&Char, 1 * sizeof(WCHAR),
Popup->CurrentCommand, FMCFL_JUST_LOOKING);
if (Index != -1) {
UpdateCommandListPopup(
(SHORT)(Index - Popup->CurrentCommand),
&Popup->CurrentCommand,
CommandHistory,
Popup,
CookedReadData->ScreenInfo, UCLP_WRAP);
}
}
}
}
NTSTATUS
ProcessCopyFromCharInput(
IN PVOID CookedReadDataPtr,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the delete from cursor to char char popup. It returns
when we're out of input or the user has entered a char.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
CONSOLE_STATUS_READ_COMPLETE - user hit return
--*/
{
NTSTATUS Status;
PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
WCHAR Char;
PHANDLE_DATA HandleData;
int i; // char index (not byte)
UNREFERENCED_PARAMETER(WaitingThread);
Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
CookedReadData->HandleIndex,
&HandleData);
if (!NT_SUCCESS(Status)) {
return Status;
}
while (TRUE) {
Status = GetChar(CookedReadData->InputInfo,
&Char,
TRUE,
CookedReadData->Console,
HandleData,
WaitReplyMessage,
CookedReadWaitRoutine,
CookedReadData,
sizeof(*CookedReadData),
WaitRoutine,
NULL,
NULL,
NULL,
NULL
);
if (!NT_SUCCESS(Status)) {
if (Status != CONSOLE_STATUS_WAIT) {
CookedReadData->BytesRead = 0;
}
return Status;
}
EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
//
// delete from cursor up to specified char
//
for (i=CookedReadData->CurrentPosition+1;
i<(int)(CookedReadData->BytesRead/sizeof(WCHAR));
i++) {
if (CookedReadData->BackupLimit[i] == Char) {
break;
}
}
if (i!=(int)(CookedReadData->BytesRead/sizeof(WCHAR)+1)) {
COORD CursorPosition;
//
// save cursor position
//
CursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
//
// deletecommandline
//
DeleteCommandLine(CookedReadData,
FALSE);
//
// delete chars
//
RtlCopyMemory(&CookedReadData->BackupLimit[CookedReadData->CurrentPosition],
&CookedReadData->BackupLimit[i],
CookedReadData->BytesRead-(i*sizeof(WCHAR))
);
CookedReadData->BytesRead -= (i-CookedReadData->CurrentPosition)*sizeof(WCHAR);
//
// write commandline
//
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
NULL
);
UserAssert(NT_SUCCESS(Status));
}
//
// restore cursor position
//
Status = SetCursorPosition(CookedReadData->ScreenInfo,
CursorPosition,
TRUE);
UserAssert(NT_SUCCESS(Status));
}
HandleData->InputReadData->ReadCount += 1;
return CONSOLE_STATUS_WAIT_NO_BLOCK;
}
}
NTSTATUS
ProcessCopyToCharInput(
IN PVOID CookedReadDataPtr,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the delete char popup. It returns
when we're out of input or the user has entered a char.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
CONSOLE_STATUS_READ_COMPLETE - user hit return
--*/
{
NTSTATUS Status;
PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
WCHAR Char;
PCOMMAND LastCommand;
DWORD NumSpaces;
SHORT ScrollY=0;
PHANDLE_DATA HandleData;
Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
CookedReadData->HandleIndex,
&HandleData);
if (!NT_SUCCESS(Status)) {
return Status;
}
while (TRUE) {
Status = GetChar(CookedReadData->InputInfo,
&Char,
TRUE,
CookedReadData->Console,
HandleData,
WaitReplyMessage,
CookedReadWaitRoutine,
CookedReadData,
sizeof(*CookedReadData),
WaitRoutine,
NULL,
NULL,
NULL,
NULL
);
if (!NT_SUCCESS(Status)) {
if (Status != CONSOLE_STATUS_WAIT) {
CookedReadData->BytesRead = 0;
}
return Status;
}
EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
//
// copy up to specified char
//
LastCommand = GetLastCommand(CookedReadData->CommandHistory);
if (LastCommand) {
int i,j;
//
// find specified char in last command
//
for (i=CookedReadData->CurrentPosition+1;i<(int)(LastCommand->CommandLength/sizeof(WCHAR));i++) {
if (LastCommand->Command[i] == Char)
break;
}
//
// if we found it, copy up to it
//
if (i<(int)(LastCommand->CommandLength/sizeof(WCHAR)) && (USHORT)(LastCommand->CommandLength/sizeof(WCHAR)) > (USHORT)CookedReadData->CurrentPosition) {
j=i-CookedReadData->CurrentPosition;
UserAssert(j > 0);
RtlCopyMemory(CookedReadData->BufPtr,
&LastCommand->Command[CookedReadData->CurrentPosition],
j*sizeof(WCHAR));
CookedReadData->CurrentPosition += j;
j*=sizeof(WCHAR);
CookedReadData->BytesRead = max(CookedReadData->BytesRead,
CookedReadData->CurrentPosition * sizeof(WCHAR));
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
(PDWORD) &j,
(PLONG)&NumSpaces,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
CookedReadData->NumberOfVisibleChars += NumSpaces;
}
CookedReadData->BufPtr+=j/sizeof(WCHAR);
}
}
HandleData->InputReadData->ReadCount += 1;
return CONSOLE_STATUS_WAIT_NO_BLOCK;
}
UNREFERENCED_PARAMETER(WaitingThread);
}
NTSTATUS
ProcessCommandNumberInput(
IN PVOID CookedReadDataPtr,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the delete char popup. It returns
when we're out of input or the user has entered a char.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
CONSOLE_STATUS_READ_COMPLETE - user hit return
--*/
{
NTSTATUS Status;
PCLE_POPUP Popup;
PCOMMAND_HISTORY CommandHistory;
PCOOKED_READ_DATA CookedReadData=(PCOOKED_READ_DATA)CookedReadDataPtr;
WCHAR Char;
DWORD NumSpaces;
BOOLEAN CommandLinePopupKeys;
SHORT CommandNumber;
PHANDLE_DATA HandleData;
CommandHistory = CookedReadData->CommandHistory;
Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
Status = DereferenceIoHandleNoCheck(CookedReadData->ProcessData,
CookedReadData->HandleIndex,
&HandleData);
UserAssert(NT_SUCCESS(Status));
while (TRUE) {
Status = GetChar(CookedReadData->InputInfo,
&Char,
TRUE,
CookedReadData->Console,
HandleData,
WaitReplyMessage,
CookedReadWaitRoutine,
CookedReadData,
sizeof(*CookedReadData),
WaitRoutine,
NULL,
&CommandLinePopupKeys,
NULL,
NULL
);
if (!NT_SUCCESS(Status)) {
if (Status != CONSOLE_STATUS_WAIT) {
CookedReadData->BytesRead = 0;
}
return Status;
}
if (Char >= (WCHAR)0x30 && Char <= (WCHAR)0x39) {
if (Popup->NumberRead < 5) {
DWORD CharsToWrite;
WORD RealAttributes;
RealAttributes = CookedReadData->ScreenInfo->Attributes;
CookedReadData->ScreenInfo->Attributes = Popup->Attributes;
CharsToWrite = sizeof(WCHAR);
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
Popup->NumberBuffer,
&Popup->NumberBuffer[Popup->NumberRead],
&Char,
&CharsToWrite,
(PLONG)&NumSpaces,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
NULL);
UserAssert(NT_SUCCESS(Status));
CookedReadData->ScreenInfo->Attributes = RealAttributes;
Popup->NumberBuffer[Popup->NumberRead] = Char;
Popup->NumberRead += 1;
}
} else if (Char == UNICODE_BACKSPACE) {
if (Popup->NumberRead > 0) {
DWORD CharsToWrite;
WORD RealAttributes;
RealAttributes = CookedReadData->ScreenInfo->Attributes;
CookedReadData->ScreenInfo->Attributes = Popup->Attributes;
CharsToWrite = sizeof(WCHAR);
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
Popup->NumberBuffer,
&Popup->NumberBuffer[Popup->NumberRead],
&Char,
&CharsToWrite,
(PLONG)&NumSpaces,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
NULL
);
UserAssert(NT_SUCCESS(Status));
CookedReadData->ScreenInfo->Attributes = RealAttributes;
Popup->NumberBuffer[Popup->NumberRead] = (WCHAR)' ';
Popup->NumberRead -= 1;
}
} else if (Char == (WCHAR)VK_ESCAPE) {
EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
if (!CLE_NO_POPUPS(CommandHistory)) {
EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
}
DeleteCommandLine(CookedReadData,
TRUE);
} else if (Char == UNICODE_CARRIAGERETURN) {
CHAR NumberBuffer[6];
int i;
for (i=0;i<Popup->NumberRead;i++) {
NumberBuffer[i] = (CHAR)Popup->NumberBuffer[i];
}
NumberBuffer[i] = 0;
CommandNumber = (SHORT)atoi(NumberBuffer);
if ((WORD)CommandNumber >= (WORD)CookedReadData->CommandHistory->NumberOfCommands) {
CommandNumber = (SHORT)(CookedReadData->CommandHistory->NumberOfCommands-1);
}
EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
if (!CLE_NO_POPUPS(CommandHistory)) {
EndPopup(CookedReadData->ScreenInfo,CookedReadData->CommandHistory);
}
SetCurrentCommandLine(CookedReadData,COMMAND_NUM_TO_INDEX(CommandNumber,CookedReadData->CommandHistory));
}
HandleData->InputReadData->ReadCount += 1;
return CONSOLE_STATUS_WAIT_NO_BLOCK;
}
UNREFERENCED_PARAMETER(WaitingThread);
}
NTSTATUS
CommandListPopup(
IN PCOOKED_READ_DATA CookedReadData,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the command list popup. It puts up the
popup, then calls ProcessCommandListInput to get and process
input.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
STATUS_SUCCESS - read was fully completed (user hit return)
--*/
{
SHORT CurrentCommand;
PCLE_POPUP Popup;
PCOMMAND_HISTORY CommandHistory;
CommandHistory = CookedReadData->CommandHistory;
Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
CurrentCommand = COMMAND_INDEX_TO_NUM(CommandHistory->LastDisplayed,CommandHistory);
if (CurrentCommand < (SHORT)(CommandHistory->NumberOfCommands - POPUP_SIZE_Y(Popup))) {
Popup->BottomIndex = (SHORT)(max(CurrentCommand,POPUP_SIZE_Y(Popup)-1));
} else {
Popup->BottomIndex = (SHORT)(CommandHistory->NumberOfCommands-1);
}
Popup->CurrentCommand = CommandHistory->LastDisplayed;
DrawCommandListPopup(Popup,
CommandHistory->LastDisplayed,
CommandHistory,
CookedReadData->ScreenInfo);
Popup->PopupInputRoutine = ProcessCommandListInput;
return ProcessCommandListInput(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine);
}
VOID
DrawPromptPopup(
IN PCLE_POPUP Popup,
IN PSCREEN_INFORMATION ScreenInfo,
IN PWCHAR Prompt,
IN ULONG PromptLength // in chars
)
{
ULONG lStringLength;
COORD WriteCoord;
SHORT i;
//
// draw empty popup
//
WriteCoord.X = (SHORT)(Popup->Region.Left+1);
WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
lStringLength = POPUP_SIZE_X(Popup);
for (i=0;i<POPUP_SIZE_Y(Popup);i++) {
FillOutput(ScreenInfo,
Popup->Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&lStringLength
);
FillOutput(ScreenInfo,
(WCHAR)' ',
WriteCoord,
CONSOLE_FALSE_UNICODE, // faster that real unicode
&lStringLength
);
WriteCoord.Y += 1;
}
WriteCoord.X = (SHORT)(Popup->Region.Left+1);
WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
//
// write prompt to screen
//
lStringLength = PromptLength;
if (lStringLength > (ULONG)POPUP_SIZE_X(Popup))
lStringLength = (ULONG)(POPUP_SIZE_X(Popup));
WriteOutputString(ScreenInfo,
Prompt,
WriteCoord,
CONSOLE_REAL_UNICODE,
&lStringLength,
NULL
);
}
NTSTATUS
CopyFromCharPopup(
IN PCOOKED_READ_DATA CookedReadData,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the "delete up to this char" popup. It puts up the
popup, then calls ProcessCopyFromCharInput to get and process
input.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
STATUS_SUCCESS - read was fully completed (user hit return)
--*/
{
PCLE_POPUP Popup;
PCOMMAND_HISTORY CommandHistory;
WCHAR ItemString[70];
int ItemLength;
NTSTATUS Status;
LANGID LangId;
Status = GetConsoleLangId(CookedReadData->ScreenInfo->Console->OutputCP, &LangId);
if (NT_SUCCESS(Status)) {
ItemLength = LoadStringEx(ghInstance,msgCmdLineF4,ItemString,70,LangId);
}
if (!NT_SUCCESS(Status) || ItemLength == 0) {
ItemLength = LoadString(ghInstance,msgCmdLineF4,ItemString,70);
}
CommandHistory = CookedReadData->CommandHistory;
Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
DrawPromptPopup(Popup,
CookedReadData->ScreenInfo,
ItemString,
ItemLength
);
Popup->PopupInputRoutine = ProcessCopyFromCharInput;
return ProcessCopyFromCharInput(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine);
}
NTSTATUS
CopyToCharPopup(
IN PCOOKED_READ_DATA CookedReadData,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the "delete up to this char" popup. It puts up the
popup, then calls ProcessCopyToCharInput to get and process
input.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
STATUS_SUCCESS - read was fully completed (user hit return)
--*/
{
PCLE_POPUP Popup;
PCOMMAND_HISTORY CommandHistory;
WCHAR ItemString[70];
int ItemLength;
NTSTATUS Status;
LANGID LangId;
Status = GetConsoleLangId(CookedReadData->ScreenInfo->Console->OutputCP, &LangId);
if (NT_SUCCESS(Status)) {
ItemLength = LoadStringEx(ghInstance,msgCmdLineF2,ItemString,70,LangId);
}
if (!NT_SUCCESS(Status) || ItemLength == 0) {
ItemLength = LoadString(ghInstance,msgCmdLineF2,ItemString,70);
}
CommandHistory = CookedReadData->CommandHistory;
Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
DrawPromptPopup(Popup,
CookedReadData->ScreenInfo,
ItemString,
ItemLength
);
Popup->PopupInputRoutine = ProcessCopyToCharInput;
return ProcessCopyToCharInput(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine);
}
NTSTATUS
CommandNumberPopup(
IN PCOOKED_READ_DATA CookedReadData,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine handles the "enter command number" popup. It puts up the
popup, then calls ProcessCommandNumberInput to get and process
input.
Return Value:
CONSOLE_STATUS_WAIT - we ran out of input, so
a wait block was created
STATUS_SUCCESS - read was fully completed (user hit return)
--*/
{
PCLE_POPUP Popup;
PCOMMAND_HISTORY CommandHistory;
COORD CursorPosition;
WCHAR ItemString[70];
int ItemLength;
NTSTATUS Status;
LANGID LangId;
CommandHistory = CookedReadData->CommandHistory;
Popup = CONTAINING_RECORD( CommandHistory->PopupList.Flink, CLE_POPUP, ListLink );
Status = GetConsoleLangId(CookedReadData->ScreenInfo->Console->OutputCP, &LangId);
if (NT_SUCCESS(Status)) {
ItemLength = LoadStringEx(ghInstance,msgCmdLineF9,ItemString,70,LangId);
}
if (!NT_SUCCESS(Status) || ItemLength == 0) {
ItemLength = LoadString(ghInstance,msgCmdLineF9,ItemString,70);
}
if (ItemLength > POPUP_SIZE_X(Popup) - COMMAND_NUMBER_LENGTH) {
ItemLength = POPUP_SIZE_X(Popup) - COMMAND_NUMBER_LENGTH;
}
DrawPromptPopup(Popup,
CookedReadData->ScreenInfo,
ItemString,
ItemLength
);
CursorPosition.X = (SHORT)(Popup->Region.Right - MINIMUM_COMMAND_PROMPT_SIZE);
CursorPosition.Y = (SHORT)(Popup->Region.Top+1);
SetCursorPosition(CookedReadData->ScreenInfo,
CursorPosition,
TRUE
);
Popup->NumberRead=0;
Popup->PopupInputRoutine = ProcessCommandNumberInput;
return ProcessCommandNumberInput(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine);
}
PCOMMAND
GetLastCommand(
IN PCOMMAND_HISTORY CommandHistory)
{
if (CommandHistory->NumberOfCommands == 0) {
return NULL;
} else {
return CommandHistory->Commands[CommandHistory->LastDisplayed];
}
}
VOID
EmptyCommandHistory(
IN PCOMMAND_HISTORY CommandHistory)
{
SHORT i;
if (CommandHistory == NULL) {
return;
}
for (i = 0; i < CommandHistory->NumberOfCommands; i++) {
ConsoleHeapFree(CommandHistory->Commands[i]);
}
CommandHistory->NumberOfCommands = 0;
CommandHistory->LastAdded = -1;
CommandHistory->LastDisplayed = -1;
CommandHistory->FirstCommand = 0;
CommandHistory->Flags = CLE_RESET;
}
BOOL
AtFirstCommand(
IN PCOMMAND_HISTORY CommandHistory)
{
SHORT i;
if (CommandHistory == NULL) {
return FALSE;
}
if (CommandHistory->Flags & CLE_RESET) {
return FALSE;
}
i = (SHORT)(CommandHistory->LastDisplayed - 1);
if (i == -1) {
i = (SHORT)(CommandHistory->NumberOfCommands - 1);
}
return (i == CommandHistory->LastAdded);
}
BOOL
AtLastCommand(
IN PCOMMAND_HISTORY CommandHistory)
{
if (CommandHistory == NULL) {
return FALSE;
} else {
return (CommandHistory->LastDisplayed == CommandHistory->LastAdded);
}
}
NTSTATUS
ProcessCommandLine(
IN PCOOKED_READ_DATA CookedReadData,
IN WCHAR Char,
IN DWORD KeyState,
IN PCSR_API_MSG WaitReplyMessage,
IN PCSR_THREAD WaitingThread,
IN BOOLEAN WaitRoutine
)
/*++
This routine process command line editing keys.
Return Value:
CONSOLE_STATUS_WAIT - CommandListPopup ran out of input
CONSOLE_STATUS_READ_COMPLETE - user hit <enter> in CommandListPopup
STATUS_SUCCESS - everything's cool
--*/
{
COORD CurrentPosition;
DWORD CharsToWrite;
NTSTATUS Status;
BOOL UpdateCursorPosition;
SHORT ScrollY=0;
BOOL fStartFromDelim;
UpdateCursorPosition = FALSE;
if (Char == VK_F7 &&
!(KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))) {
COORD PopupSize;
if (CookedReadData->CommandHistory &&
CookedReadData->CommandHistory->NumberOfCommands) {
PopupSize.X = 40;
PopupSize.Y = 10;
Status = BeginPopup(CookedReadData->ScreenInfo,
CookedReadData->CommandHistory,
PopupSize
);
if (NT_SUCCESS(Status)) {
// CommandListPopup does EndPopup call
return CommandListPopup(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine
);
}
}
} else {
switch (Char) {
case VK_ESCAPE:
DeleteCommandLine(CookedReadData,
TRUE);
break;
case VK_UP:
case VK_DOWN:
case VK_F5:
if (Char == VK_F5)
Char = VK_UP;
// for doskey compatibility, buffer isn't circular
if (Char==VK_UP && !AtFirstCommand(CookedReadData->CommandHistory) ||
Char==VK_DOWN && !AtLastCommand(CookedReadData->CommandHistory)) {
DeleteCommandLine(CookedReadData,
TRUE);
Status = RetrieveCommand(CookedReadData->CommandHistory,
Char,
CookedReadData->BackupLimit,
CookedReadData->BufferSize,
&CookedReadData->BytesRead);
UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY );
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
}
CharsToWrite = CookedReadData->BytesRead/sizeof(WCHAR);
CookedReadData->CurrentPosition = CharsToWrite;
CookedReadData->BufPtr = CookedReadData->BackupLimit + CharsToWrite;
}
break;
case VK_PRIOR:
case VK_NEXT:
if (CookedReadData->CommandHistory &&
CookedReadData->CommandHistory->NumberOfCommands) {
//
// display oldest or newest command
//
SHORT CommandNumber;
if (Char == VK_PRIOR) {
CommandNumber = 0;
} else {
CommandNumber = (SHORT)(CookedReadData->CommandHistory->NumberOfCommands-1);
}
DeleteCommandLine(CookedReadData,
TRUE);
Status = RetrieveNthCommand(CookedReadData->CommandHistory,
COMMAND_NUM_TO_INDEX(CommandNumber,CookedReadData->CommandHistory),
CookedReadData->BackupLimit,
CookedReadData->BufferSize,
&CookedReadData->BytesRead);
UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY
);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
}
CharsToWrite = CookedReadData->BytesRead/sizeof(WCHAR);
CookedReadData->CurrentPosition = CharsToWrite;
CookedReadData->BufPtr = CookedReadData->BackupLimit + CharsToWrite;
}
break;
case VK_END:
if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
DeleteCommandLine(CookedReadData,
FALSE);
CookedReadData->BytesRead = CookedReadData->CurrentPosition*sizeof(WCHAR);
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
NULL
);
UserAssert(NT_SUCCESS(Status));
}
} else {
CookedReadData->CurrentPosition = CookedReadData->BytesRead/sizeof(WCHAR);
CookedReadData->BufPtr = CookedReadData->BackupLimit + CookedReadData->CurrentPosition;
CurrentPosition.X = (SHORT)(CookedReadData->OriginalCursorPosition.X + CookedReadData->NumberOfVisibleChars);
CurrentPosition.Y = CookedReadData->OriginalCursorPosition.Y;
#if defined(FE_SB)
if (CheckBisectProcessW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition,
CookedReadData->ScreenInfo->ScreenBufferSize.X-CookedReadData->OriginalCursorPosition.X,
CookedReadData->OriginalCursorPosition.X,
TRUE)) {
CurrentPosition.X++;
}
#endif
UpdateCursorPosition = TRUE;
}
break;
case VK_HOME:
if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
DeleteCommandLine(CookedReadData,
FALSE);
CookedReadData->BytesRead -= CookedReadData->CurrentPosition*sizeof(WCHAR);
CookedReadData->CurrentPosition = 0;
RtlCopyMemory(CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BytesRead
);
CookedReadData->BufPtr = CookedReadData->BackupLimit;
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
NULL
);
UserAssert(NT_SUCCESS(Status));
}
CurrentPosition = CookedReadData->OriginalCursorPosition;
UpdateCursorPosition = TRUE;
} else {
CookedReadData->CurrentPosition = 0;
CookedReadData->BufPtr = CookedReadData->BackupLimit;
CurrentPosition = CookedReadData->OriginalCursorPosition;
UpdateCursorPosition = TRUE;
}
break;
case VK_LEFT:
if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
PWCHAR LastWord;
BOOL NonSpaceCharSeen=FALSE;
if (CookedReadData->BufPtr != CookedReadData->BackupLimit) {
if (!gExtendedEditKey) {
LastWord = CookedReadData->BufPtr-1;
while (LastWord != CookedReadData->BackupLimit) {
if (!IS_WORD_DELIM(*LastWord)) {
NonSpaceCharSeen=TRUE;
} else {
if (NonSpaceCharSeen) {
break;
}
}
LastWord--;
}
if (LastWord != CookedReadData->BackupLimit) {
CookedReadData->BufPtr = LastWord+1;
} else {
CookedReadData->BufPtr = LastWord;
}
} else {
/*
* A bit better word skipping.
*/
LastWord = CookedReadData->BufPtr - 1;
if (LastWord != CookedReadData->BackupLimit) {
if (*LastWord == L' ') {
/*
* Skip spaces, until the non-space character is found.
*/
while (--LastWord != CookedReadData->BackupLimit) {
UserAssert(LastWord > CookedReadData->BackupLimit);
if (*LastWord != L' ') {
break;
}
}
}
if (LastWord != CookedReadData->BackupLimit) {
if (IS_WORD_DELIM(*LastWord)) {
/*
* Skip WORD_DELIMs until space or non WORD_DELIM is found.
*/
while (--LastWord != CookedReadData->BackupLimit) {
UserAssert(LastWord > CookedReadData->BackupLimit);
if (*LastWord == L' ' || !IS_WORD_DELIM(*LastWord)) {
break;
}
}
} else {
/*
* Skip the regular words
*/
while (--LastWord != CookedReadData->BackupLimit) {
UserAssert(LastWord > CookedReadData->BackupLimit);
if (IS_WORD_DELIM(*LastWord)) {
break;
}
}
}
}
}
UserAssert(LastWord >= CookedReadData->BackupLimit);
if (LastWord != CookedReadData->BackupLimit) {
/*
* LastWord is currently pointing to the last character
* of the previous word, unless it backed up to the beginning
* of the buffer.
* Let's increment LastWord so that it points to the expeced
* insertion point.
*/
++LastWord;
}
CookedReadData->BufPtr = LastWord;
}
CookedReadData->CurrentPosition=(ULONG)(CookedReadData->BufPtr-CookedReadData->BackupLimit);
CurrentPosition = CookedReadData->OriginalCursorPosition;
// FE_SB
CurrentPosition.X = (SHORT)(CurrentPosition.X +
RetrieveTotalNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition,
CookedReadData->ScreenInfo->Console));
if (CheckBisectStringW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition+1,
CookedReadData->ScreenInfo->ScreenBufferSize.X
-CookedReadData->OriginalCursorPosition.X
)) {
CurrentPosition.X++;
}
// end FE_SB
UpdateCursorPosition = TRUE;
}
} else {
if (CookedReadData->BufPtr != CookedReadData->BackupLimit) {
CookedReadData->BufPtr--;
CookedReadData->CurrentPosition--;
CurrentPosition.X = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition.X;
CurrentPosition.Y = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition.Y;
#if defined(FE_SB)
CurrentPosition.X = (SHORT)(CurrentPosition.X -
RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition,
CookedReadData->ScreenInfo->Console,
CookedReadData->ScreenInfo->Console->CP));
if (CheckBisectProcessW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition+2,
CookedReadData->ScreenInfo->ScreenBufferSize.X
-CookedReadData->OriginalCursorPosition.X,
CookedReadData->OriginalCursorPosition.X,
TRUE)) {
if ((CurrentPosition.X == -2) ||
(CurrentPosition.X == -1)) {
CurrentPosition.X--;
}
}
#else
CurrentPosition.X = (SHORT)(CurrentPosition.X - RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition));
#endif
UpdateCursorPosition = TRUE;
}
}
break;
case VK_RIGHT:
case VK_F1:
//
// we don't need to check for end of buffer here because we've
// already done it.
//
if (KeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
if (Char != VK_F1) {
if (CookedReadData->CurrentPosition < (CookedReadData->BytesRead/sizeof(WCHAR))) {
PWCHAR NextWord = CookedReadData->BufPtr;
if (!gExtendedEditKey) {
SHORT i;
for (i=(SHORT)(CookedReadData->CurrentPosition);
i<(SHORT)((CookedReadData->BytesRead-1)/sizeof(WCHAR));
i++) {
if (IS_WORD_DELIM(*NextWord)) {
i++;
NextWord++;
while ((i<(SHORT)((CookedReadData->BytesRead-1)/sizeof(WCHAR))) &&
IS_WORD_DELIM(*NextWord)) {
i++;
NextWord++;
}
break;
}
NextWord++;
}
} else {
/*
* A bit better word skipping.
*/
PWCHAR BufLast = CookedReadData->BackupLimit + CookedReadData->BytesRead / sizeof(WCHAR);
UserAssert(NextWord < BufLast);
if (*NextWord == L' ') {
/*
* If the current character is space,
* skip to the next non-space character.
*/
while (NextWord < BufLast) {
if (*NextWord != L' ') {
break;
}
++NextWord;
}
} else {
/*
* Skip the body part.
*/
BOOL fStartFromDelim = IS_WORD_DELIM(*NextWord);
while (++NextWord < BufLast) {
if (fStartFromDelim != IS_WORD_DELIM(*NextWord)) {
break;
}
}
/*
* Skip the space block.
*/
if (NextWord < BufLast && *NextWord == L' ') {
while (++NextWord < BufLast) {
if (*NextWord != L' ') {
break;
}
}
}
}
}
CookedReadData->BufPtr = NextWord;
CookedReadData->CurrentPosition=(ULONG)(CookedReadData->BufPtr-CookedReadData->BackupLimit);
// FE_SB
CurrentPosition = CookedReadData->OriginalCursorPosition;
CurrentPosition.X = (SHORT)(CurrentPosition.X +
RetrieveTotalNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition,
CookedReadData->ScreenInfo->Console));
if (CheckBisectStringW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition+1,
CookedReadData->ScreenInfo->ScreenBufferSize.X
-CookedReadData->OriginalCursorPosition.X
)) {
CurrentPosition.X++;
}
// end FE_SB
UpdateCursorPosition = TRUE;
}
}
} else {
//
// if not at the end of the line, move cursor position right
//
if (CookedReadData->CurrentPosition < (CookedReadData->BytesRead/sizeof(WCHAR))) {
CurrentPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
#if defined(FE_SB)
CurrentPosition.X = (SHORT)(CurrentPosition.X +
RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition,
CookedReadData->ScreenInfo->Console,
CookedReadData->ScreenInfo->Console->CP));
if (CheckBisectProcessW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition+2,
CookedReadData->ScreenInfo->ScreenBufferSize.X
-CookedReadData->OriginalCursorPosition.X,
CookedReadData->OriginalCursorPosition.X,
TRUE)) {
if (CurrentPosition.X == (CookedReadData->ScreenInfo->ScreenBufferSize.X-1))
CurrentPosition.X++;
}
#else
CurrentPosition.X = (SHORT)(CurrentPosition.X + RetrieveNumberOfSpaces(CookedReadData->OriginalCursorPosition.X,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition));
#endif
CookedReadData->BufPtr++;
CookedReadData->CurrentPosition++;
UpdateCursorPosition = TRUE;
//
// if at the end of the line, copy a character from the
// same position in the last command
//
} else if (CookedReadData->CommandHistory) {
PCOMMAND LastCommand;
DWORD NumSpaces;
LastCommand = GetLastCommand(CookedReadData->CommandHistory);
if (LastCommand && (USHORT)(LastCommand->CommandLength/sizeof(WCHAR)) > (USHORT)CookedReadData->CurrentPosition) {
*CookedReadData->BufPtr = LastCommand->Command[CookedReadData->CurrentPosition];
CookedReadData->BytesRead += sizeof(WCHAR);
CookedReadData->CurrentPosition++;
if (CookedReadData->Echo) {
CharsToWrite = sizeof(WCHAR);
Status = WriteCharsFromInput(
CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
&CharsToWrite,
(PLONG)&NumSpaces,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
CookedReadData->NumberOfVisibleChars += NumSpaces;
}
CookedReadData->BufPtr+=1;
}
}
}
break;
case VK_F2:
//
// copy the previous command to the current command, up to but
// not including the character specified by the user. the user
// is prompted via popup to enter a character.
//
if (CookedReadData->CommandHistory) {
COORD PopupSize;
PopupSize.X = COPY_TO_CHAR_PROMPT_LENGTH+2;
PopupSize.Y = 1;
Status = BeginPopup(CookedReadData->ScreenInfo,
CookedReadData->CommandHistory,
PopupSize
);
if (NT_SUCCESS(Status)) {
// CopyToCharPopup does EndPopup call
return CopyToCharPopup(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine
);
}
}
break;
case VK_F3:
//
// copy the remainder of the previous command to the current command.
//
if (CookedReadData->CommandHistory) {
PCOMMAND LastCommand;
DWORD NumSpaces;
int j; // chars, not bytes
LastCommand = GetLastCommand(CookedReadData->CommandHistory);
if (LastCommand && (USHORT)(LastCommand->CommandLength/sizeof(WCHAR)) > (USHORT)CookedReadData->CurrentPosition) {
j = (LastCommand->CommandLength/sizeof(WCHAR)) - CookedReadData->CurrentPosition;
RtlCopyMemory(CookedReadData->BufPtr,
&LastCommand->Command[CookedReadData->CurrentPosition],
j*sizeof(WCHAR)
);
CookedReadData->CurrentPosition += j;
j *= sizeof(WCHAR);
CookedReadData->BytesRead += j;
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
(PDWORD) &j,
(PLONG)&NumSpaces,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
CookedReadData->NumberOfVisibleChars += NumSpaces;
}
CookedReadData->BufPtr+=j/sizeof(WCHAR);
}
}
break;
case VK_F4:
//
// copy the previous command to the current command, from
// the letter specified by the user. the user
// is prompted via popup to enter a character.
//
if (CookedReadData->CommandHistory) {
COORD PopupSize;
PopupSize.X = COPY_FROM_CHAR_PROMPT_LENGTH+2;
PopupSize.Y = 1;
Status = BeginPopup(CookedReadData->ScreenInfo,
CookedReadData->CommandHistory,
PopupSize
);
if (NT_SUCCESS(Status)) {
// CopyFromCharPopup does EndPopup call
return CopyFromCharPopup(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine
);
}
}
break;
case VK_F6:
//
// place a ctrl-z in the current command line
//
{
DWORD NumSpaces;
*CookedReadData->BufPtr = (WCHAR)0x1a; // ctrl-z
CookedReadData->BytesRead += sizeof(WCHAR);
CookedReadData->CurrentPosition++;
if (CookedReadData->Echo) {
CharsToWrite = sizeof(WCHAR);
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
&CharsToWrite,
(PLONG)&NumSpaces,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY
);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
CookedReadData->NumberOfVisibleChars += NumSpaces;
}
CookedReadData->BufPtr+=1;
}
break;
case VK_F7:
if (KeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) {
if (CookedReadData->CommandHistory) {
EmptyCommandHistory(CookedReadData->CommandHistory);
CookedReadData->CommandHistory->Flags |= CLE_ALLOCATED;
}
}
break;
case VK_F8:
if (CookedReadData->CommandHistory) {
SHORT i;
//
// cycles through the stored commands that start with
// the characters in the current command
//
i = FindMatchingCommand(CookedReadData->CommandHistory,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition*sizeof(WCHAR),
CookedReadData->CommandHistory->LastDisplayed, 0);
if (i!=-1) {
SHORT CurrentPosition;
COORD CursorPosition;
//
// save cursor position
//
CurrentPosition = (SHORT)CookedReadData->CurrentPosition;
CursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
DeleteCommandLine(CookedReadData,
TRUE);
Status = RetrieveNthCommand(CookedReadData->CommandHistory,
i,
CookedReadData->BackupLimit,
CookedReadData->BufferSize,
&CookedReadData->BytesRead);
UserAssert(CookedReadData->BackupLimit == CookedReadData->BufPtr);
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BufPtr,
CookedReadData->BufPtr,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&ScrollY);
UserAssert(NT_SUCCESS(Status));
CookedReadData->OriginalCursorPosition.Y += ScrollY;
}
CursorPosition.Y += ScrollY;
//
// restore cursor position
//
CookedReadData->BufPtr = CookedReadData->BackupLimit + CurrentPosition;
CookedReadData->CurrentPosition = CurrentPosition;
Status = SetCursorPosition(CookedReadData->ScreenInfo,
CursorPosition,
TRUE);
UserAssert(NT_SUCCESS(Status));
}
}
break;
case VK_F9:
//
// prompt the user to enter the desired command number.
// copy that command to the command line.
//
{
COORD PopupSize;
if (CookedReadData->CommandHistory &&
CookedReadData->CommandHistory->NumberOfCommands &&
CookedReadData->ScreenInfo->ScreenBufferSize.X >= MINIMUM_COMMAND_PROMPT_SIZE+2) { // 2 is for border
PopupSize.X = COMMAND_NUMBER_PROMPT_LENGTH+COMMAND_NUMBER_LENGTH;
PopupSize.Y = 1;
Status = BeginPopup(CookedReadData->ScreenInfo,
CookedReadData->CommandHistory,
PopupSize
);
if (NT_SUCCESS(Status)) {
// CommandNumberPopup does EndPopup call
return CommandNumberPopup(CookedReadData,
WaitReplyMessage,
WaitingThread,
WaitRoutine
);
}
}
}
break;
case VK_F10:
if (KeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) {
ClearAliases(CookedReadData->Console);
}
break;
case VK_INSERT:
CookedReadData->InsertMode = !CookedReadData->InsertMode;
SetCursorMode(CookedReadData->ScreenInfo,
(BOOLEAN)(CookedReadData->InsertMode != CookedReadData->Console->InsertMode));
break;
case VK_DELETE:
if (!AT_EOL(CookedReadData)) {
COORD CursorPosition;
fStartFromDelim = IS_WORD_DELIM(*CookedReadData->BufPtr);
del_repeat:
//
// save cursor position
//
CursorPosition = CookedReadData->ScreenInfo->BufferInfo.TextInfo.CursorPosition;
//
// deletecommandline
//
DeleteCommandLine(CookedReadData,
FALSE);
//
// delete char
//
CookedReadData->BytesRead -= sizeof(WCHAR);
RtlCopyMemory(CookedReadData->BufPtr,
CookedReadData->BufPtr+1,
CookedReadData->BytesRead - (CookedReadData->CurrentPosition*sizeof(WCHAR))
);
#if defined(FE_SB)
{
PWCHAR buf = (PWCHAR)((PBYTE)CookedReadData->BackupLimit +
CookedReadData->BytesRead );
*buf = (WCHAR)' ';
}
#endif
//
// write commandline
//
if (CookedReadData->Echo) {
Status = WriteCharsFromInput(CookedReadData->ScreenInfo,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
CookedReadData->BackupLimit,
&CookedReadData->BytesRead,
(PLONG)&CookedReadData->NumberOfVisibleChars,
CookedReadData->OriginalCursorPosition.X,
WC_DESTRUCTIVE_BACKSPACE |
WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
NULL);
UserAssert(NT_SUCCESS(Status));
}
//
// restore cursor position
//
if (CONSOLE_IS_DBCS_ENABLED() && CONSOLE_IS_DBCS_CP(CookedReadData->Console)) {
if (CheckBisectProcessW(CookedReadData->ScreenInfo,
CookedReadData->ScreenInfo->Console->CP,
CookedReadData->BackupLimit,
CookedReadData->CurrentPosition+1,
CookedReadData->ScreenInfo->ScreenBufferSize.X
-CookedReadData->OriginalCursorPosition.X,
CookedReadData->OriginalCursorPosition.X,
TRUE)) {
CursorPosition.X++;
}
CurrentPosition = CursorPosition;
if (CookedReadData->Echo) {
Status = AdjustCursorPosition(CookedReadData->ScreenInfo,
CurrentPosition,
TRUE,
NULL);
UserAssert(NT_SUCCESS(Status));
}
} else {
Status = SetCursorPosition(CookedReadData->ScreenInfo,
CursorPosition,
TRUE);
UserAssert(NT_SUCCESS(Status));
}
// If Ctrl key is pressed, delete a word.
// If the start point was word delimiter, just remove delimiters portion only.
if ((KeyState & CTRL_PRESSED) && !AT_EOL(CookedReadData) &&
fStartFromDelim ^ !IS_WORD_DELIM(*CookedReadData->BufPtr)) {
DBGPRINT(("Repeating it(%x).\n", *CookedReadData->BufPtr));
goto del_repeat;
}
}
break;
default:
UserAssert(FALSE);
break;
}
}
if (UpdateCursorPosition && CookedReadData->Echo) {
Status = AdjustCursorPosition(CookedReadData->ScreenInfo,
CurrentPosition,
TRUE,
NULL);
UserAssert(NT_SUCCESS(Status));
}
return STATUS_SUCCESS;
}
PCOMMAND RemoveCommand(
IN PCOMMAND_HISTORY CommandHistory,
IN SHORT iDel)
{
SHORT iFirst, iLast, iDisp, nDel;
PCOMMAND *ppcFirst, *ppcDel, pcmdDel;
iFirst = CommandHistory->FirstCommand;
iLast = CommandHistory->LastAdded;
iDisp = CommandHistory->LastDisplayed;
if (CommandHistory->NumberOfCommands == 0) {
return NULL;
}
nDel = COMMAND_INDEX_TO_NUM(iDel, CommandHistory);
if ((nDel < COMMAND_INDEX_TO_NUM(iFirst, CommandHistory)) ||
(nDel > COMMAND_INDEX_TO_NUM(iLast, CommandHistory))) {
return NULL;
}
if (iDisp == iDel) {
CommandHistory->LastDisplayed = -1;
}
ppcFirst = &(CommandHistory->Commands[iFirst]);
ppcDel = &(CommandHistory->Commands[iDel]);
pcmdDel = *ppcDel;
if (iDel < iLast) {
RtlCopyMemory(ppcDel, ppcDel+1, (iLast - iDel) * sizeof(PCOMMAND));
if ((iDisp > iDel) && (iDisp <= iLast)) {
COMMAND_IND_DEC(iDisp, CommandHistory);
}
COMMAND_IND_DEC(iLast, CommandHistory);
} else if (iFirst <= iDel) {
RtlMoveMemory(ppcFirst+1, ppcFirst, (iDel - iFirst) * sizeof(PCOMMAND));
if ((iDisp >= iFirst) && (iDisp < iDel)) {
COMMAND_IND_INC(iDisp, CommandHistory);
}
COMMAND_IND_INC(iFirst, CommandHistory);
}
CommandHistory->FirstCommand = iFirst;
CommandHistory->LastAdded = iLast;
CommandHistory->LastDisplayed = iDisp;
CommandHistory->NumberOfCommands--;
return pcmdDel;
}
SHORT
FindMatchingCommand(
IN PCOMMAND_HISTORY CommandHistory,
IN PWCHAR pwszIn,
IN ULONG cbIn, // in bytes (!)
IN SHORT CommandIndex, // where to start from
IN DWORD Flags
)
/*++
this routine finds the most recent command that starts with
the letters already in the current command. it returns the
array index (no mod needed).
--*/
{
SHORT i;
if (CommandHistory->NumberOfCommands == 0) {
return -1;
}
if (!(Flags & FMCFL_JUST_LOOKING) && (CommandHistory->Flags & CLE_RESET)) {
CommandHistory->Flags &= ~CLE_RESET;
} else {
COMMAND_IND_PREV(CommandIndex, CommandHistory);
}
if (cbIn == 0) {
return CommandIndex;
}
for (i=0;i<CommandHistory->NumberOfCommands;i++) {
PCOMMAND pcmdT = CommandHistory->Commands[CommandIndex];
if ((!(Flags & FMCFL_EXACT_MATCH) && (cbIn <= pcmdT->CommandLength)) ||
((USHORT)cbIn == pcmdT->CommandLength)) {
if (!my_wcsncmp(pcmdT->Command, pwszIn, (USHORT)cbIn)) {
return CommandIndex;
}
}
COMMAND_IND_PREV(CommandIndex, CommandHistory);
}
return -1;
}
VOID
DrawCommandListBorder(
IN PCLE_POPUP Popup,
IN PSCREEN_INFORMATION ScreenInfo
)
{
COORD WriteCoord;
ULONG Length;
SHORT i;
//
// fill attributes of top line
//
WriteCoord.X = Popup->Region.Left;
WriteCoord.Y = Popup->Region.Top;
Length = POPUP_SIZE_X(Popup) + 2;
FillOutput(ScreenInfo,
Popup->Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&Length
);
//
// draw upper left corner
//
Length = 1;
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[UPPER_LEFT_CORNER],
#else
(WCHAR)0x250c,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
//
// draw upper bar
//
WriteCoord.X += 1;
Length = POPUP_SIZE_X(Popup);
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[HORIZONTAL_LINE],
#else
(WCHAR)0x2500,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
//
// draw upper right corner
//
WriteCoord.X = Popup->Region.Right;
Length = 1;
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[UPPER_RIGHT_CORNER],
#else
(WCHAR)0x2510,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
for (i=0;i<POPUP_SIZE_Y(Popup);i++) {
WriteCoord.Y += 1;
WriteCoord.X = Popup->Region.Left;
//
// fill attributes
//
Length = POPUP_SIZE_X(Popup) + 2;
FillOutput(ScreenInfo,
Popup->Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&Length
);
Length = 1;
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[VERTICAL_LINE],
#else
(WCHAR)0x2502,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
WriteCoord.X = Popup->Region.Right;
Length = 1;
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[VERTICAL_LINE],
#else
(WCHAR)0x2502,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
}
//
// draw bottom line
//
// fill attributes of top line
//
WriteCoord.X = Popup->Region.Left;
WriteCoord.Y = Popup->Region.Bottom;
Length = POPUP_SIZE_X(Popup) + 2;
FillOutput(ScreenInfo,
Popup->Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&Length
);
//
// draw bottom left corner
//
Length = 1;
WriteCoord.X = Popup->Region.Left;
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[BOTTOM_LEFT_CORNER],
#else
(WCHAR)0x2514,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
//
// draw lower bar
//
WriteCoord.X += 1;
Length = POPUP_SIZE_X(Popup);
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[HORIZONTAL_LINE],
#else
(WCHAR)0x2500,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
//
// draw lower right corner
//
WriteCoord.X = Popup->Region.Right;
Length = 1;
FillOutput(ScreenInfo,
#if defined(FE_SB)
ScreenInfo->LineChar[BOTTOM_RIGHT_CORNER],
#else
(WCHAR)0x2518,
#endif
WriteCoord,
CONSOLE_REAL_UNICODE,
&Length
);
}
VOID
UpdateHighlight(
IN PCLE_POPUP Popup,
IN SHORT OldCurrentCommand, // command number, not index
IN SHORT NewCurrentCommand,
IN PSCREEN_INFORMATION ScreenInfo
)
{
COORD WriteCoord;
ULONG lStringLength;
WORD Attributes;
SHORT TopIndex;
if (Popup->BottomIndex < POPUP_SIZE_Y(Popup)) {
TopIndex = 0;
} else {
TopIndex = (SHORT)(Popup->BottomIndex-POPUP_SIZE_Y(Popup)+1);
}
WriteCoord.X = (SHORT)(Popup->Region.Left+1);
lStringLength = POPUP_SIZE_X(Popup);
WriteCoord.Y = (SHORT)(Popup->Region.Top+1+OldCurrentCommand-TopIndex);
FillOutput(ScreenInfo,
Popup->Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&lStringLength
);
//
// highlight new command
//
WriteCoord.Y = (SHORT)(Popup->Region.Top+1+NewCurrentCommand-TopIndex);
// inverted attributes
Attributes = (WORD)(((Popup->Attributes << 4) & 0xf0) |
((Popup->Attributes >> 4) & 0x0f));
FillOutput(ScreenInfo,
Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&lStringLength
);
}
VOID
DrawCommandListPopup(
IN PCLE_POPUP Popup,
IN SHORT CurrentCommand,
IN PCOMMAND_HISTORY CommandHistory,
IN PSCREEN_INFORMATION ScreenInfo
)
{
WORD Attributes;
ULONG lStringLength,CommandNumberLength;
CHAR CommandNumber[COMMAND_NUMBER_SIZE];
PCHAR CommandNumberPtr;
COORD WriteCoord;
SHORT i;
//
// draw empty popup
//
WriteCoord.X = (SHORT)(Popup->Region.Left+1);
WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
lStringLength = POPUP_SIZE_X(Popup);
for (i=0;i<POPUP_SIZE_Y(Popup);i++) {
FillOutput(ScreenInfo,
Popup->Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&lStringLength
);
FillOutput(ScreenInfo,
(WCHAR)' ',
WriteCoord,
CONSOLE_FALSE_UNICODE, // faster than real unicode
&lStringLength
);
WriteCoord.Y += 1;
}
WriteCoord.Y = (SHORT)(Popup->Region.Top+1);
for (i=max((SHORT)(Popup->BottomIndex-POPUP_SIZE_Y(Popup)+1),0);i<=Popup->BottomIndex;i++) {
//
// write command number to screen
//
CommandNumberPtr = _itoa(i,CommandNumber,10);
CommandNumberLength = (SHORT)lstrlenA(CommandNumberPtr);
CommandNumber[CommandNumberLength] = ':';
CommandNumber[CommandNumberLength+1] = ' ';
CommandNumberLength+=2;
if (CommandNumberLength > (ULONG)POPUP_SIZE_X(Popup))
CommandNumberLength = (ULONG)POPUP_SIZE_X(Popup);
WriteCoord.X = (SHORT)(Popup->Region.Left+1);
WriteOutputString(ScreenInfo,
CommandNumberPtr,
WriteCoord,
CONSOLE_ASCII,
&CommandNumberLength,
NULL
);
//
// write command to screen
//
lStringLength = CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->CommandLength/sizeof(WCHAR);
#if defined(FE_SB)
{
DWORD lTmpStringLength;
LONG lPopupLength;
LPWSTR lpStr;
lTmpStringLength = lStringLength;
lPopupLength = POPUP_SIZE_X(Popup) - CommandNumberLength;
lpStr = CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command;
while (lTmpStringLength--) {
if (IsConsoleFullWidth(ScreenInfo->Console->hDC,
ScreenInfo->Console->OutputCP,*lpStr++)) {
lPopupLength -= 2;
} else {
lPopupLength--;
}
if (lPopupLength <= 0) {
lStringLength -= lTmpStringLength;
if (lPopupLength < 0)
lStringLength--;
break;
}
}
}
#else
if ((lStringLength+CommandNumberLength) > (ULONG)POPUP_SIZE_X(Popup))
lStringLength = (ULONG)(POPUP_SIZE_X(Popup)-CommandNumberLength);
#endif
WriteCoord.X = (SHORT)(WriteCoord.X + CommandNumberLength);
#if defined(FE_SB)
{
PWCHAR TransBuffer;
TransBuffer = ConsoleHeapAlloc(TMP_DBCS_TAG, lStringLength * sizeof(WCHAR));
if (TransBuffer == NULL) {
return;
}
RtlCopyMemory(TransBuffer,CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command,lStringLength * sizeof(WCHAR));
WriteOutputString(ScreenInfo,
TransBuffer,
WriteCoord,
CONSOLE_REAL_UNICODE,
&lStringLength,
NULL
);
ConsoleHeapFree(TransBuffer);
}
#else
WriteOutputString(ScreenInfo,
CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command,
WriteCoord,
CONSOLE_REAL_UNICODE,
&lStringLength,
NULL
);
// convert back to true unicode (got converted by WriteOutputString)
if ((ScreenInfo->Flags & CONSOLE_OEMFONT_DISPLAY) &&
!(ScreenInfo->Console->FullScreenFlags & CONSOLE_FULLSCREEN)) {
FalseUnicodeToRealUnicode(CommandHistory->Commands[COMMAND_NUM_TO_INDEX(i,CommandHistory)]->Command,
lStringLength,
ScreenInfo->Console->OutputCP);
}
#endif
//
// write attributes to screen
//
if (COMMAND_NUM_TO_INDEX(i,CommandHistory) == CurrentCommand) {
WriteCoord.X = (SHORT)(Popup->Region.Left+1);
// inverted attributes
Attributes = (WORD)(((Popup->Attributes << 4) & 0xf0) |
((Popup->Attributes >> 4) & 0x0f));
lStringLength = POPUP_SIZE_X(Popup);
FillOutput(ScreenInfo,
Attributes,
WriteCoord,
CONSOLE_ATTRIBUTE,
&lStringLength
);
}
WriteCoord.Y += 1;
}
}
VOID
UpdateCommandListPopup(
IN SHORT Delta,
IN OUT PSHORT CurrentCommand, // real index, not command #
IN PCOMMAND_HISTORY CommandHistory,
IN PCLE_POPUP Popup,
IN PSCREEN_INFORMATION ScreenInfo,
IN DWORD Flags
)
{
SHORT Size;
SHORT CurCmdNum;
SHORT NewCmdNum;
BOOL Scroll=FALSE;
if (Delta == 0) {
return;
}
Size = POPUP_SIZE_Y(Popup);
if (Flags & UCLP_WRAP) {
CurCmdNum = *CurrentCommand;
NewCmdNum = CurCmdNum + Delta;
NewCmdNum = COMMAND_INDEX_TO_NUM(NewCmdNum, CommandHistory);
CurCmdNum = COMMAND_INDEX_TO_NUM(CurCmdNum, CommandHistory);
} else {
CurCmdNum = COMMAND_INDEX_TO_NUM(*CurrentCommand, CommandHistory);
NewCmdNum = CurCmdNum + Delta;
if (NewCmdNum >= CommandHistory->NumberOfCommands) {
NewCmdNum = (SHORT)(CommandHistory->NumberOfCommands-1);
} else if (NewCmdNum < 0) {
NewCmdNum = 0;
}
}
Delta = NewCmdNum - CurCmdNum;
// determine amount to scroll, if any
if (NewCmdNum <= Popup->BottomIndex-Size) {
Popup->BottomIndex += Delta;
if (Popup->BottomIndex < (SHORT)(Size-1)) {
Popup->BottomIndex = (SHORT)(Size-1);
}
Scroll = TRUE;
} else if (NewCmdNum > Popup->BottomIndex) {
Popup->BottomIndex += Delta;
if (Popup->BottomIndex >= CommandHistory->NumberOfCommands) {
Popup->BottomIndex = (SHORT)(CommandHistory->NumberOfCommands-1);
}
Scroll = TRUE;
}
//
// write commands to popup
//
if (Scroll) {
DrawCommandListPopup(Popup,COMMAND_NUM_TO_INDEX(NewCmdNum,CommandHistory),CommandHistory,ScreenInfo);
} else {
UpdateHighlight(Popup,COMMAND_INDEX_TO_NUM((*CurrentCommand),CommandHistory),NewCmdNum,ScreenInfo);
}
*CurrentCommand = COMMAND_NUM_TO_INDEX(NewCmdNum,CommandHistory);
}
PCOMMAND_HISTORY
FindCommandHistory(
IN PCONSOLE_INFORMATION Console,
IN HANDLE ProcessHandle
)
/*++
Routine Description:
This routine marks the command history buffer freed.
Arguments:
Console - pointer to console.
ProcessHandle - handle to client process.
Return Value:
none.
--*/
{
PCOMMAND_HISTORY History;
PLIST_ENTRY ListHead, ListNext;
ListHead = &Console->CommandHistoryList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
History = CONTAINING_RECORD(ListNext, COMMAND_HISTORY, ListLink);
ListNext = ListNext->Flink;
if (History->ProcessHandle == ProcessHandle) {
UserAssert(History->Flags & CLE_ALLOCATED);
return History;
}
}
return NULL;
}
VOID
FreeCommandHistory(
IN PCONSOLE_INFORMATION Console,
IN HANDLE ProcessHandle
)
/*++
Routine Description:
This routine marks the command history buffer freed.
Arguments:
Console - pointer to console.
ProcessHandle - handle to client process.
Return Value:
none.
--*/
{
PCOMMAND_HISTORY History;
History = FindCommandHistory(Console,ProcessHandle);
if (History) {
History->Flags &= ~CLE_ALLOCATED;
History->ProcessHandle = NULL;
}
}
VOID
FreeCommandHistoryBuffers(
IN OUT PCONSOLE_INFORMATION Console
)
{
PCOMMAND_HISTORY History;
PLIST_ENTRY ListHead, ListNext;
SHORT i;
ListHead = &Console->CommandHistoryList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
ListNext = ListNext->Flink;
RemoveEntryList(&History->ListLink);
if (History->AppName) {
ConsoleHeapFree(History->AppName);
}
for (i=0;i<History->NumberOfCommands;i++) {
ConsoleHeapFree(History->Commands[i]);
}
ConsoleHeapFree(History);
}
}
VOID
ResizeCommandHistoryBuffers(
IN PCONSOLE_INFORMATION Console,
IN UINT NumCommands
)
{
PCOMMAND_HISTORY History;
PLIST_ENTRY ListHead, ListNext;
#if defined(FE_SB)
PCOOKED_READ_DATA CookedReadData;
PCOMMAND_HISTORY NewHistory;
#endif
UserAssert(NumCommands <= 0xFFFF);
Console->CommandHistorySize = (SHORT)NumCommands;
ListHead = &Console->CommandHistoryList;
ListNext = ListHead->Flink;
while (ListNext != ListHead) {
History = CONTAINING_RECORD( ListNext, COMMAND_HISTORY, ListLink );
ListNext = ListNext->Flink;
#if defined(FE_SB)
NewHistory = ReallocCommandHistory(Console, History, NumCommands);
CookedReadData = Console->lpCookedReadData;
if (CookedReadData && CookedReadData->CommandHistory == History) {
CookedReadData->CommandHistory = NewHistory;
}
#else
if (!(History->Flags & CLE_ALLOCATED)) {
ReallocCommandHistory(Console, History, NumCommands);
}
#endif
}
}
VOID
InitializeConsoleCommandData(
IN PCONSOLE_INFORMATION Console
)
/*++
Routine Description:
This routine initializes the per-console commandline recall data structures.
Arguments:
Console - pointer to console.
Return Value:
none
--*/
{
Console->NumCommandHistories = 0;
InitializeListHead(&Console->CommandHistoryList);
}
VOID
ResetCommandHistory(
IN PCOMMAND_HISTORY CommandHistory
)
/*++
This routine is called when escape is entered or a command is added.
--*/
{
if (CommandHistory == NULL) {
return;
}
CommandHistory->LastDisplayed = CommandHistory->LastAdded;
CommandHistory->Flags |= CLE_RESET;
}
NTSTATUS
AddCommand(
IN PCOMMAND_HISTORY CommandHistory,
IN PWCHAR Command,
IN USHORT Length,
IN BOOL HistoryNoDup
)
{
PCOMMAND *ppCmd;
if (CommandHistory == NULL || CommandHistory->MaximumNumberOfCommands == 0) {
return STATUS_NO_MEMORY;
}
UserAssert(CommandHistory->Flags & CLE_ALLOCATED);
if (Length == 0) {
return STATUS_SUCCESS;
}
if (CommandHistory->NumberOfCommands == 0 ||
CommandHistory->Commands[CommandHistory->LastAdded]->CommandLength != Length ||
memcmp(CommandHistory->Commands[CommandHistory->LastAdded]->Command,Command,Length)) {
PCOMMAND pCmdReuse = NULL;
if (HistoryNoDup) {
SHORT i;
i = FindMatchingCommand(CommandHistory, Command, Length,
CommandHistory->LastDisplayed, FMCFL_EXACT_MATCH);
if (i != -1) {
pCmdReuse = RemoveCommand(CommandHistory, i);
}
}
//
// find free record. if all records are used, free the lru one.
//
if (CommandHistory->NumberOfCommands < CommandHistory->MaximumNumberOfCommands) {
CommandHistory->LastAdded += 1;
CommandHistory->NumberOfCommands++;
} else {
COMMAND_IND_INC(CommandHistory->LastAdded, CommandHistory);
COMMAND_IND_INC(CommandHistory->FirstCommand, CommandHistory);
ConsoleHeapFree(CommandHistory->Commands[CommandHistory->LastAdded]);
if (CommandHistory->LastDisplayed == CommandHistory->LastAdded) {
CommandHistory->LastDisplayed = -1;
}
}
if (CommandHistory->LastDisplayed == -1 ||
CommandHistory->Commands[CommandHistory->LastDisplayed]->CommandLength != Length ||
memcmp(CommandHistory->Commands[CommandHistory->LastDisplayed]->Command,Command,Length)) {
ResetCommandHistory(CommandHistory);
}
//
// add command to array
//
ppCmd = &CommandHistory->Commands[CommandHistory->LastAdded];
if (pCmdReuse) {
*ppCmd = pCmdReuse;
} else {
*ppCmd = ConsoleHeapAlloc(HISTORY_TAG,
Length + sizeof(COMMAND));
if (*ppCmd == NULL) {
COMMAND_IND_PREV(CommandHistory->LastAdded, CommandHistory);
CommandHistory->NumberOfCommands -= 1;
return STATUS_NO_MEMORY;
}
(*ppCmd)->CommandLength = Length;
RtlCopyMemory((*ppCmd)->Command,Command,Length);
}
}
CommandHistory->Flags |= CLE_RESET; // remember that we've returned a cmd
return STATUS_SUCCESS;
}
NTSTATUS
RetrieveCommand(
IN PCOMMAND_HISTORY CommandHistory,
IN WORD VirtualKeyCode,
IN PWCHAR Buffer,
IN ULONG BufferSize,
OUT PULONG CommandSize)
{
if (CommandHistory == NULL) {
return STATUS_UNSUCCESSFUL;
}
UserAssert(CommandHistory->Flags & CLE_ALLOCATED);
if (CommandHistory->NumberOfCommands == 0) {
return STATUS_UNSUCCESSFUL;
}
if (CommandHistory->NumberOfCommands == 1) {
CommandHistory->LastDisplayed = 0;
} else if (VirtualKeyCode == VK_UP) {
//
// if this is the first time for this read that a command has
// been retrieved, return the current command. otherwise, return
// the previous command.
//
if (CommandHistory->Flags & CLE_RESET) {
CommandHistory->Flags &= ~CLE_RESET;
} else {
COMMAND_IND_PREV(CommandHistory->LastDisplayed, CommandHistory);
}
} else {
COMMAND_IND_NEXT(CommandHistory->LastDisplayed, CommandHistory);
}
return RetrieveNthCommand(CommandHistory,
CommandHistory->LastDisplayed,
Buffer,
BufferSize,
CommandSize
);
}
ULONG
SrvGetConsoleTitle(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_GETTITLE_MSG a = (PCONSOLE_GETTITLE_MSG)&m->u.ApiMessageData;
NTSTATUS Status;
PCONSOLE_INFORMATION Console;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Title, a->TitleLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
// a->TitleLength contains length in bytes
if (a->Unicode) {
if ((USHORT)a->TitleLength > Console->TitleLength) {
a->TitleLength = Console->TitleLength;
}
RtlCopyMemory(a->Title,Console->Title,a->TitleLength);
} else {
#if defined(FE_SB)
a->TitleLength = (USHORT)ConvertToOem(OEMCP,
Console->Title,
Console->TitleLength / sizeof(WCHAR),
a->Title,
a->TitleLength
);
#else
a->TitleLength = (USHORT)ConvertToOem(Console->CP,
Console->Title,
Console->TitleLength / sizeof(WCHAR),
a->Title,
a->TitleLength
);
#endif
}
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus); // get rid of unreferenced parameter warning message
}
ULONG
SrvSetConsoleTitle(
IN OUT PCSR_API_MSG m,
IN OUT PCSR_REPLY_STATUS ReplyStatus
)
{
PCONSOLE_SETTITLE_MSG a = (PCONSOLE_SETTITLE_MSG)&m->u.ApiMessageData;
NTSTATUS Status;
PCONSOLE_INFORMATION Console;
LPWSTR NewTitle;
Status = ApiPreamble(a->ConsoleHandle,
&Console
);
if (!NT_SUCCESS(Status)) {
return Status;
}
if (!CsrValidateMessageBuffer(m, &a->Title, a->TitleLength, sizeof(BYTE))) {
UnlockConsole(Console);
return STATUS_INVALID_PARAMETER;
}
if (!a->Unicode) {
NewTitle = ConsoleHeapAlloc(TITLE_TAG,
a->TitleLength * sizeof(WCHAR) + sizeof(WCHAR));
if (NewTitle == NULL) {
UnlockConsole(Console);
return STATUS_NO_MEMORY;
}
// convert title to unicode
#if defined(FE_SB)
Console->TitleLength = (USHORT)ConvertInputToUnicode(OEMCP,
a->Title,
a->TitleLength,
NewTitle,
a->TitleLength);
#else
Console->TitleLength = (USHORT)ConvertInputToUnicode(Console->CP,
a->Title,
a->TitleLength,
NewTitle,
a->TitleLength);
#endif
Console->TitleLength *= 2;
} else {
// a->TitleLength contains length in bytes
NewTitle = ConsoleHeapAlloc(TITLE_TAG, a->TitleLength + sizeof(WCHAR));
if (NewTitle == NULL) {
UnlockConsole(Console);
return STATUS_NO_MEMORY;
}
Console->TitleLength = (USHORT)a->TitleLength;
RtlCopyMemory(NewTitle,a->Title,a->TitleLength);
}
NewTitle[Console->TitleLength/sizeof(WCHAR)] = 0; // NULL terminate
ConsoleHeapFree(Console->Title);
Console->Title = NewTitle;
PostMessage(Console->hWnd, CM_UPDATE_TITLE, 0, 0);
UnlockConsole(Console);
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(ReplyStatus); // get rid of unreferenced parameter warning message
}
int
LoadStringExW(
IN HINSTANCE hModule,
IN UINT wID,
OUT LPWSTR lpBuffer,
IN int cchBufferMax,
IN WORD wLangId
)
{
HANDLE hResInfo;
HANDLE hStringSeg;
LPTSTR lpsz;
int cch;
/*
* Make sure the parms are valid.
*/
if (lpBuffer == NULL) {
return 0;
}
cch = 0;
/*
* String Tables are broken up into 16 string segments. Find the segment
* containing the string we are interested in.
*/
if (hResInfo = FindResourceEx(hModule,
RT_STRING,
(LPTSTR)((LONG_PTR)(((USHORT)wID >> 4) + 1)),
wLangId)) {
/*
* Load that segment.
*/
hStringSeg = LoadResource(hModule, hResInfo);
/*
* Lock the resource.
*/
if (lpsz = (LPTSTR)LockResource(hStringSeg)) {
/*
* Move past the other strings in this segment.
* (16 strings in a segment -> & 0x0F)
*/
wID &= 0x0F;
while (TRUE) {
cch = *((UTCHAR *)lpsz++); // PASCAL like string count
// first UTCHAR is count if TCHARs
if (wID-- == 0) break;
lpsz += cch; // Step to start if next string
}
/*
* chhBufferMax == 0 means return a pointer to the read-only resource buffer.
*/
if (cchBufferMax == 0) {
*(LPTSTR *)lpBuffer = lpsz;
} else {
/*
* Account for the NULL
*/
cchBufferMax--;
/*
* Don't copy more than the max allowed.
*/
if (cch > cchBufferMax)
cch = cchBufferMax;
/*
* Copy the string into the buffer.
*/
RtlCopyMemory(lpBuffer, lpsz, cch*sizeof(WCHAR));
}
}
}
/*
* Append a NULL.
*/
if (cchBufferMax != 0) {
lpBuffer[cch] = 0;
}
return cch;
}