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
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;
|
|
}
|