Windows2000/private/windbg64/windbg/cmdwin.c
2020-09-30 17:12:32 +02:00

2366 lines
64 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
cmdwin.c
Abstract:
This file contains the window procedure code for dealing with the
command window. This window uses the document manager to deal with
keeping track of the characters in the buffer.
This window has the following strange properties:
It is read-only except on the last line
The first portion of the last line is read-only
Author:
Jim Schaad (jimsch)
Environment:
Win32 - User
--*/
/************ INCLUDE FILES ******/
#include "precomp.h"
#pragma hdrstop
#include <ime.h>
extern LPSHF Lpshf;
extern CXF CxfIp;
/************ Externals ***********/
BOOL FCmdDoingInput;
extern BOOL fWaitForDebugString;
extern LPSTR lpCmdString;
extern ADDR addrLastDisasmStart;
/************ Internal Prototypes ***********/
/************ Data declaration ***********/
static BOOL FAutoRunSuppress = FALSE;
PCTRLC_HANDLER pPolledCtrlCHandler = NULL;
BOOL fCtrlCPressed = FALSE; // Ctrl-C pressed flag for EditWndProc
static PCTRLC_HANDLER PcchHead = NULL;
static BOOL fEatEOLWhitespace = TRUE; // eat space at end of line
static BOOL FAutoHistOK = FALSE;
static char szOldPrompt[PROMPT_SIZE];
/************ Code ***********/
void NEAR PASCAL SelPosXY (int view, int X, int Y);
void
BPCallbackHbpt(
HBPT hBpt,
BPSTATUS bpstatus
)
/*++
Routine Description:
This function will be called for each breakpoint which
mets its criteria
Arguments:
hBpt - Supplies breakpoint which met the breakpoint criteria
bpstatus - Supplies breakpoint status code during evaluation
Return Value:
None
--*/
{
ULONG i;
char rgchT[256];
if (bpstatus != BPNOERROR) {
Dbg( BPIFromHbpt( &i, hBpt ) == BPNOERROR);
CmdInsertInit();
CmdLogFmt("Error checking breakpoint #%d\r\n", i);
return;
}
if (LptdCur->fInFuncEval == FALSE) {
Dbg( BPIFromHbpt( &i, hBpt) == BPNOERROR );
CmdInsertInit();
CmdLogFmt("Breakpoint #%d hit\r\n", i);
Dbg( BPQueryCmdOfHbpt( hBpt, rgchT, sizeof(rgchT)-1) == BPNOERROR );
if (*rgchT != 0) {
CmdPrependCommands(LptdCur, rgchT);
}
}
return;
} /* BPCallbackHbpt() */
BOOL
DoStopEvent(
LPTD lptd
)
/*++
Routine Description:
This routine is called whenever a thread stops. It handles any special
processing that should be done when a thread stops, such as print the
registers or executing special commands associated with stopping.
Arguments:
lptd - Supplies the thread that stopped
Return Value:
TRUE if the caller should be silent.
FALSE if normal processing should continue, and normal messages
should be printed.
--*/
{
if (*szStopEventCmd) {
CmdPrependCommands(lptd, szStopEventCmd);
}
if (lptd && lptd->fDisasm && !AutoTest) {
CmdPrependCommands(lptd, "u . l1");
lptd->fDisasm = FALSE;
}
if (lptd && lptd->fRegisters) {
CmdPrependCommands(lptd, "r");
lptd->fRegisters = FALSE;
}
// forget where we disassembled last, so that "u" will default to
// the current IP.
addrLastDisasmStart.addr.seg = 0;
addrLastDisasmStart.addr.off = 0;
return FALSE;
}
BOOL
CmdHandleInputString(
LPSTR lpsz
)
{
CmdSetDefaultPrompt( szOldPrompt );
CmdSetDefaultCmdProc();
OSDInfoReply(LppdCur->hpid, LptdCur->htid, lpsz, strlen(lpsz) + 1);
return TRUE;
}
VOID
GetPointersFromQueue(
HPID * phpid,
HTID * phtid,
LPPD * plppd,
LPTD * plptd
)
{
*plptd = (LPTD) GetQueueItemQuad();
if (*plptd != 0) {
*plppd = (*plptd)->lppd;
*phtid = (*plptd)->htid;
*phpid = (*plppd)->hpid;
} else {
*phpid = (HPID)GetQueueItemQuad();
*phtid = (HTID)GetQueueItemQuad();
*plppd = LppdOfHpid(*phpid);
Assert(*plppd);
*plptd = LptdOfLppdHtid(*plppd, *phtid);
}
}
BOOL
CmdExecNext(
DBC unusedIW,
LPARAM unusedL
)
/*++
Routine Description:
This function causes two things to occur. First the command window
is updated to reflect the command/event which just occurred and
secondly the command processor state machine is consulted to see
if there are any more commands to be executed from the command
window.
Arguments:
dbcCallback - Supplies the call back which caused this update to occur
lParam - Supplies information about the callback
Return Value:
TRUE if update the screen and FALSE otherwise
--*/
{
long lExit;
LPSTR lpch;
LPSTR lpsz;
long l;
DWORD dw;
int dbcCallback;
HEXE emi;
SHE she;
BPSTATUS bpstatus;
XOSD xosd;
CXT cxt;
ADDR addr;
DWORD64 qwNotify;
EXOP exop;
HPDS HpdsOld;
HPID hpid;
HTID htid;
LPPD lppd;
LPTD lptd;
LPPD LppdT;
LPTD LptdT;
BOOL fExecNext;
int bfRefresh;
BOOL fGetFocus;
BOOL fRunning;
BOOL fReply;
int iter = 0;
BOOL ThreadIsStopped;
static int fRecurse = FALSE;
extern BOOL FKilling;
if (fRecurse) {
return TRUE;
}
fRecurse = TRUE;
// This call will AV if
__try {
while (GetQueueLength() != 0) {
if (iter++ == 10) {
fRecurse = FALSE;
PostMessage(hwndFrame, DBG_REFRESH, 0, 0);
return FALSE;
}
/*
* Inititalization of variables;
*/
LppdT = LppdCur;
LptdT = LptdCur;
fExecNext = TRUE;
bfRefresh = UPDATE_ALLDBGWIN;
fGetFocus = FALSE;
/*
* Get the next command to be processed
*/
dbcCallback = (int) GetQueueItemQuad();
#if 0
// BUGBUG -- jls Need to put in new code to deal with this problem.
if ((DbgState == ds_error) && (dbcCallback != dbcInfoAvail)) {
fRecurse = FALSE;
return FALSE;
}
#endif
/*
* Set up the command window to allow for doing input.
*/
CmdInsertInit();
/*
*/
switch ((DBC)dbcCallback) {
case dbcError: /* DBG_INFO */
bfRefresh = UPDATE_NONE;
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
xosd = (XOSD)GetQueueItemQuad();
lpsz = (LPSTR)GetQueueItemQuad();
LppdCur = LppdOfHpid(hpid);
LptdCur = LptdOfLppdHtid(LppdCur, htid);
CmdLogFmt("ERROR - %s\r\n", lpsz);
MHFree(lpsz);
fGetFocus = TRUE;
fExecNext = FALSE;
if (LppdCur->pstate == psPreRunning) {
/*
* we aren't going to get the breakpoint...
*/
LppdCur->pstate = psRunning;
for (lptd = LppdCur->lptdList; lptd; lptd = lptd->lptdNext) {
lptd->tstate = tsRunning;
}
Dbg( BPFreeHbpt( (HBPT)LppdCur->hbptSaved ) == BPNOERROR) ;
LppdCur->hbptSaved = NULL;
BPTResolveAll(LppdCur->hpid,TRUE);
SetProcessExceptions(LppdCur);
VarMsgBox(NULL, DBG_Attach_Deadlock,
MB_OK | MB_ICONINFORMATION|MB_SETFOREGROUND|MB_TASKMODAL);
}
// Any time we update the context we must invalidate the Watch window.
bfRefresh = UPDATE_CONTEXT | UPDATE_ALLDBGWIN | UPDATE_SYMBOLS_CHANGED;
break;
case dbcLoadComplete:
lptd = (LPTD) GetQueueItemQuad();
Assert(lptd != NULL);
qwNotify = GetQueueItemQuad();
LptdCur = lptd;
LppdCur = lptd->lppd;
Assert(LppdCur->pstate == psPreRunning);
LptdCur->tstate = tsStopped;
// Any time we update the context we must invalidate the Watch window.
UpdateDebuggerState(UPDATE_CONTEXT | UPDATE_SYMBOLS_CHANGED | UPDATE_WATCH);
BPTResolveAll(LppdCur->hpid,TRUE);
/*
* The process is no longer "PreRunning",
* but the thread is.
*/
LppdCur->pstate = psStopped;
SetProcessExceptions(LppdCur);
#ifdef SHOW_MAGIC
CmdLogFmt("(Process loaded)\r\n");
#endif
fExecNext = FALSE;
bfRefresh = UPDATE_NONE;
/*
* Handle go/stop for child here.
* For attachee, it is handled in AttachDebuggee()
*/
if (LppdCur->fChild) {
if (g_contWorkspace_WkSp.m_bChildGo) {
Go();
} else {
DoStopEvent(lptd);
lptd->lppd->fStopAtEntry = FALSE;
fGetFocus = TRUE;
fExecNext = TRUE;
bfRefresh = UPDATE_ALLDBGWIN;
}
}
break;
case dbcEntryPoint:
lptd = (LPTD) GetQueueItemQuad();
Assert(lptd != NULL);
qwNotify = GetQueueItemQuad();
LptdCur = lptd;
lppd =
LppdCur = lptd->lppd;
LptdCur->tstate = tsStopped;
UpdateDebuggerState(UPDATE_CONTEXT);
fRunning = FALSE;
if (lppd->fInitialStep) {
// If stepping in source mode,
// we will land here.
// If we are in "regular" mode, try
// to find main or WINMAIN or whatever.
if (!g_contWorkspace_WkSp.m_bEPIsFirstStep) {
cxt = *SHpCXTFrompCXF(&CxfIp);
fRunning = get_initial_context(&cxt, FALSE);
//fRunning = get_initial_context(&cxt, TRUE);
if (fRunning) {
SYFixupAddr(SHpAddrFrompCxt(&cxt));
//v-vadimp - skip the prolog on ia64
// kentf - I think this is the wrong place to do this,
// and you probably shouldn't get the focus
// until later.
if(LppdCur->mptProcessorType == mptia64) {
while( SHIsInProlog( &cxt ) ) {
cxt.addr.addr.off++;
}
}
GoUntil( SHpAddrFrompCxt(&cxt) );
} else {
VarMsgBox (NULL,
ERR_NoSymbols,
MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND|MB_TASKMODAL);
}
fGetFocus = TRUE;
}
} else if (lppd->hbptSaved) {
if (!get_initial_context(SHpCXTFrompCXF(&CxfIp), FALSE)) {
get_initial_context(SHpCXTFrompCXF(&CxfIp), TRUE);
}
if (BPBindHbpt( (HBPT)lppd->hbptSaved, &CxfIp ) != BPNOERROR) {
Dbg( BPFreeHbpt( (HBPT)lppd->hbptSaved ) == BPNOERROR) ;
lppd->hbptSaved = NULL;
CmdLogVar(ERR_Unable_To_Complete_Gountil);
} else {
Dbg( BPAddrFromHbpt( (HBPT)lppd->hbptSaved, &addr ) == BPNOERROR) ;
Dbg( BPFreeHbpt( (HBPT)lppd->hbptSaved ) == BPNOERROR) ;
lppd->hbptSaved = NULL;
fRunning = TRUE;
GoUntil(&addr);
}
} else if (!lppd->fStopAtEntry) {
// if stepping in ASM mode, fStopAtEntry
// is like a safety BP to stop running after
// leaving the loader APC.
fRunning = TRUE;
Go();
}
if (fRunning) {
fExecNext = FALSE;
bfRefresh = UPDATE_NONE;
LppdCur = LppdT;
LptdCur = LptdT;
} else {
BPClearAllTmp( lppd->hpid, lptd->htid );
if (!DoStopEvent(lptd)) {
CmdLogVar(DBG_At_Entry_Point);
}
}
lppd->fInitialStep = FALSE;
lppd->fStopAtEntry = FALSE;
break;
case dbcCheckBpt:
Assert(!"dbcCheckBpt should not get here.");
break;
case dbcBpt: /* DBG_REFRESH */
lptd = (LPTD) GetQueueItemQuad();
Assert(lptd != NULL);
qwNotify = GetQueueItemQuad();
LptdCur = lptd;
LppdCur = lptd->lppd;
LptdCur->tstate = tsStopped;
UpdateDebuggerState(UPDATE_CONTEXT);
if ( FKilling || !DebuggeeActive() ) {
LppdCur = LppdT;
LptdCur = LptdT;
} else {
bpstatus = BPCheckHbpt( CxfIp, BPCallbackHbpt,
lptd->lppd->hpid, lptd->htid, qwNotify );
if (bpstatus == BPNoBreakpoint) {
BPClearAllTmp( lptd->lppd->hpid, lptd->htid );
DoStopEvent(lptd);
lptd->lppd->fStopAtEntry = FALSE;
fGetFocus = TRUE;
CmdLogVar(DBG_Hard_Coded_Breakpoint);
} else if (bpstatus == BPPassBreakpoint) {
/*
* -- do a go as no breakpoint was matched
*/
//Assert(!"Unexpected BPPassBreakpoint");
fExecNext = FALSE;
bfRefresh = UPDATE_NONE;
Go();
LppdCur = LppdT;
LptdCur = LptdT;
} else {
BPClearAllTmp( lptd->lppd->hpid, lptd->htid );
DoStopEvent(lptd);
lptd->lppd->fStopAtEntry = FALSE;
fGetFocus = TRUE;
}
}
break;
case dbcAsyncStop: /* DBG_REFRESH */
fExecNext = FALSE;
case dbcStep:
bfRefresh = UPDATE_ALLDBGWIN | UPDATE_CONTEXT;
lptd = (LPTD) GetQueueItemQuad();
Assert(lptd != NULL);
qwNotify = GetQueueItemQuad();
LptdCur = lptd;
LppdCur = lptd->lppd;
SetPTState(psInvalidState, tsStopped);
BPClearAllTmp( lptd->lppd->hpid, lptd->htid );
if (!DoStopEvent(lptd)) {
if (dbcCallback == dbcAsyncStop) {
CmdLogFmt("Stopped in process %d, thread %d\r\n",
lptd->lppd->ipid,
lptd->itid);
}
}
if (dbcCallback == dbcAsyncStop) {
LptdCur->cStepsLeft = 0;
}
lptd->lppd->fStopAtEntry = FALSE;
fGetFocus = TRUE;
break;
case dbcModLoad: /* DBG_INFO */
lptd = (LPTD)GetQueueItemQuad();
if (lptd == NULL) {
// this is the first modload, which is really
// part of the process creation. The thread isn't
// here yet.
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
lppd = LppdOfHpid(hpid);
lptd = LptdOfLppdHtid(lppd, htid);
} else {
lppd = lptd->lppd;
hpid = lppd->hpid;
htid = lptd->htid;
}
lpsz = (LPSTR)GetQueueItemQuad();
ThreadIsStopped = (BOOL)GetQueueItemQuad();
Assert(lppd);
Assert(lpsz);
// Any time we update the context we must invalidate the
// Watch window.
bfRefresh = UPDATE_ALLDBGWIN | UPDATE_SYMBOLS_CHANGED;
// set the SH to this process, load the symbols,
// tell the EM, and reset the SH.
HpdsOld = SHChangeProcess(lppd->hpds);
SetFindExeBaseName(lppd->lpBaseExeName);
she = SHLoadDll( lpsz, TRUE );
emi = SHGethExeFromName((char FAR *) lpsz);
Assert(emi != 0);
OSDRegisterEmi( hpid, (HEMI)emi, lpsz);
MHFree(lpsz);
SHChangeProcess(HpdsOld);
// update the modload status report
ModListModLoad( SHGetExeName( emi ), she );
// If this is the first modload, remember it as the base exe
if (!lppd->lpBaseExeName) {
lppd->lpBaseExeName = MHStrdup(SHGetExeName( emi ));
}
// spew to the cmdwin
if (she != sheSuppressSyms && g_contWorkspace_WkSp.m_bVerbose) {
lpch = SHLszGetErrorText(she);
if (she == sheNoSymbols) {
CmdLogFmt("Module Load: %s", SHGetExeName( emi ));
} else {
CmdLogFmt("Module Load: %s", SHGetSymFName( emi ));
}
if (lpch) {
CmdLogFmt(" (%s)", lpch);
}
CmdLogFmt("\r\n");
}
if (lppd->pstate != psPreRunning) {
// A new module has been loaded. If we are not loading
// the statically-linked DLLs of the debuggee, we must
// try to resolve any unresolved breakpoints.
if (BPTIsUnresolvedCount( hpid )) {
LptdCur = lptd;
LppdCur = lppd;
UpdateDebuggerState( bfRefresh );
BPTResolveAll( hpid, FALSE );
LppdCur = LppdT;
LptdCur = LptdT;
bfRefresh |= UPDATE_CONTEXT;
}
}
// Continue the thread or stay stopped:
if (!ThreadIsStopped) {
fExecNext = FALSE;
} else if (g_contWorkspace_WkSp.m_bGoOnModLoad || lptd == NULL) {
fExecNext = FALSE;
OSDGo(hpid, htid, &exop);
} else {
LptdCur = lptd;
LppdCur = lppd;
SetPTState(psInvalidState, tsStopped);
fExecNext = TRUE;
fGetFocus = TRUE;
}
// When debugging memory dumps, the loading of the kernel
// needs to be synchronous, else data structures in the DM
// will not be initialized to sane values.
if ( g_contWorkspace_WkSp.m_bKernelDebugger
&& g_contKernelDbgPreferences_WkSp.m_bUseCrashDump) {
TCHAR szFName[_MAX_FNAME];
TCHAR szExt[_MAX_EXT];
_tsplitpath(lppd->lpBaseExeName, 0, 0, szFName, szExt);
if (!_tcsicmp(szExt, _T(".EXE"))
&& (
!_tcsicmp(szFName, _T("NTOSKRNL")) ||
!_tcsicmp(szFName, _T("NTKRNLMP"))
)
) {
OSDSignalKernelLoadCompleted(hpid);
}
}
break;
case dbcModFree: /* DBG_REFRESH */
// Any time we update the context we must invalidate the
// Watch window.
bfRefresh = UPDATE_CONTEXT | UPDATE_SYMBOLS_CHANGED | UPDATE_WATCH;
lptd = (LPTD)GetQueueItemQuad();
if (lptd == NULL) {
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
lppd = LppdOfHpid(hpid);
lptd = LptdOfLppdHtid(lppd, htid);
} else {
lppd = lptd->lppd;
hpid = lppd->hpid;
htid = lptd->htid;
}
Assert(lppd);
Assert(lptd);
emi = (HEXE) GetQueueItemQuad();
Assert(emi != 0);
HpdsOld = SHChangeProcess(lppd->hpds);
lpsz = SHGetExeName( emi );
// unloading user mode modules in kernel mode
// can result in nameless emis
if (lpsz && *lpsz) {
ModListModUnload( SHGetExeName( emi ) );
}
if (g_contWorkspace_WkSp.m_bVerbose && !ExitingDebugger) {
if (SHIsDllLoaded(emi) && lpsz) {
CmdLogFmt("Module Unload: %s\r\n", lpsz);
}
}
// A module has been unloaded. We must unresolve all
// the breakpoints in the module.
if (lppd->pstate == psRunning) {
BPTUnResolve( emi );
}
OSDUnRegisterEmi(hpid, (HEMI) emi);
SHUnloadDll( emi );
fExecNext = FALSE;
break;
case dbcCreateThread: /* DBG_INFO */
bfRefresh = UPDATE_NONE;
fExecNext = FALSE;
Dbg(GetQueueItemQuad() == 0);
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
lppd = LppdOfHpid(hpid);
if (!lppd && !LppdFirst) {
// when blowing off the debuggee, some stray events
// may be in this queue...
break;
}
Assert(lppd);
lptd = CreateTd(lppd, htid);
Assert(lptd);
LppdT = LppdCur = lppd;
LptdT = LptdCur = lptd;
if (g_contWorkspace_WkSp.m_bNotifyThreadCreate) {
CmdLogFmt("Thread Create: Process=%d, Thread=%d\r\n",
lptd->lppd->ipid, lptd->itid);
}
// Don't do the update and BPTResolveAll if this is a
// thread which is being created when we are already
// stopped at a debug event.
if (!IsProcStopped(lppd)) {
UpdateDebuggerState( UPDATE_CONTEXT );
if (LppdCur->pstate == psRunning) {
BPTResolveAll( hpid, TRUE );
}
SetPTState(psInvalidState, tsStopped);
Go();
}
break;
case dbcThreadTerm: /* DBG_REFRESH */
lptd = (LPTD) GetQueueItemQuad();
if ( lptd == 0) {
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
lppd = LppdOfHpid(hpid);
lptd = LptdOfLppdHtid(lppd, htid);
}
lExit = (long) GetQueueItemQuad();
if (!lptd && !LppdFirst) {
// when blowing off the debuggee, some stray events
// may be in this queue...
break;
}
Assert(lptd != NULL);
lppd = lptd->lppd;
LppdCur = lppd;
LptdCur = lptd;
SetPTState(psInvalidState, tsExited);
if (g_contWorkspace_WkSp.m_bNotifyThreadTerm) {
CmdLogFmt("Thread Terminate: Process=%d, Thread=%d, Exit Code=%ld\r\n",
lptd->lppd->ipid, lptd->itid, lExit);
}
if (lptd->fInFuncEval) {
fExecNext = FALSE;
LptdFuncEval = NULL;
}
BPTUnResolvePidTid( lppd->hpid, lptd->htid );
if (lptd->fGoOnTerm ||
g_contWorkspace_WkSp.m_bGoOnThreadTerm ||
!g_contWorkspace_WkSp.m_bNotifyThreadTerm) {
Go();
fExecNext = FALSE;
bfRefresh = UPDATE_NONE;
} else {
DoStopEvent(NULL);
lptd->lppd->fStopAtEntry = FALSE;
fGetFocus = TRUE;
bfRefresh |= UPDATE_NOFORCE;
}
break;
case dbcDeleteThread: /* DBG_INFO */
// Any time we update the context we must invalidate the Watch window.
bfRefresh = UPDATE_WINDOWS | UPDATE_SYMBOLS_CHANGED;
lptd = (LPTD) GetQueueItemQuad();
if ( lptd ) {
lppd = lptd->lppd;
Assert(lppd != NULL);
#ifdef SHOW_MAGIC
CmdLogFmt("DBG: Thread Destroy: Process=%d, Thread=%d\r\n",
lptd->lppd->ipid, lptd->itid);
#endif
if (lptd->fInFuncEval) {
LptdFuncEval = NULL;
}
OSDDestroyHtid(lptd->lppd->hpid, lptd->htid);
DestroyTd(lptd);
if (LppdCur == lppd && LptdCur == lptd) {
LptdCur = NULL;
}
lptd = NULL;
}
fExecNext = FALSE;
break;
case dbcNewProc: /* DBG_INFO */
bfRefresh = UPDATE_NONE;
/*
* This will occur on all but the first process created.
*/
hpid = (HPID)GetQueueItemQuad();
lppd = LppdOfHpid(hpid);
Assert(lppd == NULL);
lppd = CreatePd(hpid);
SetPdInfo(lppd);
lppd->hpds = SHCreateProcess();
SHSetHpid(hpid);
SetProcessExceptions(lppd);
/*
* proc is PreRunning until ldr BP
*/
lppd->pstate = psPreRunning;
/*
* If it is an attach, not a child, AttachDebuggee() will
* clear this flag in a moment.
*/
lppd->fChild = TRUE;
CmdLogFmt("Process Create: Process=%d\r\n", lppd->ipid);
/*
* This won't be the current process yet, because
* there isn't a thread for it until we get notified.
*/
fExecNext = FALSE;
break;
case dbcProcTerm: /* DBG_INFO */
bfRefresh = UPDATE_NONE;
lppd = LppdOfHpid((HPID) GetQueueItemQuad());
LppdCur = lppd;
LptdCur = NULL;
SetPTState(psExited, tsInvalidState);
lExit = (long) GetQueueItemQuad();
if (!FKilling) {
CmdLogFmt("Process Terminate: Process=%d, Exit Code=%ld\r\n",
lppd->ipid, lExit);
}
/*
* Unresolve all the breakpoints. This way they can be
* resolved again upon restarting.
*/
BPClearAllTmp( lppd->hpid, 0 );
BPTUnResolveAll(lppd->hpid);
fExecNext = FALSE;
LppdCur = NULL;
LptdCur = NULL;
bfRefresh |= UPDATE_NOFORCE;
break;
case dbcDeleteProc: /* DBG_INFO */
bfRefresh = UPDATE_NONE;
hpid = (HPID)GetQueueItemQuad();
lppd = LppdOfHpid(hpid);
#ifdef SHOW_MAGIC
CmdLogFmt("DBG: Process Destroy: Process=%d\r\n", lppd->ipid);
#endif
SHChangeProcess(lppd->hpds);
while ( emi = SHGetNextExe( (HEXE) NULL ) ) {
SHUnloadDll( emi );
}
ClearProcessExceptions(lppd);
if (!lppd->fPrecious) {
SHDeleteProcess(lppd->hpds);
OSDDestroyHpid(hpid);
}
DestroyPd(lppd, FALSE);
RecycleIpid1();
if (lppd == LppdCur) {
LppdCur = NULL;
LptdCur = NULL;
} else if (LppdCur) {
SHChangeProcess(LppdCur->hpds);
// Any time we update the context we must invalidate the Watch window.
bfRefresh |= (UPDATE_NOFORCE | UPDATE_CONTEXT | UPDATE_WINDOWS | UPDATE_SYMBOLS_CHANGED);
}
break;
case dbcInfoAvail: /* DBG_INFO */
bfRefresh = UPDATE_NONE;
fExecNext = FALSE;
fReply = (BOOL) GetQueueItemQuad(); // Reply flag
if (!GetQueueItemQuad()) { /* fUniCode */
// ANSI
lpsz = (LPSTR) GetQueueItemQuad(); /* String */
CmdLogDebugString( lpsz, TRUE );
} else {
// Unicode
LPWSTR lpw;
lpw = (LPWSTR) GetQueueItemQuad(); /* String */
l = WideCharToMultiByte(CP_ACP,
0,
lpw,
-1,
NULL,
0,
NULL,
NULL
);
lpsz = (PSTR) MHAlloc(l+1);
Assert(lpsz);
WideCharToMultiByte(CP_ACP,
0,
lpw,
-1,
lpsz,
l+1,
NULL,
NULL
);
CmdLogDebugString(lpsz, TRUE);
MHFree(lpw);
}
if (fWaitForDebugString) {
char *lpsz1=lpsz;
while (lpsz1 && *lpsz1) {
if (*lpsz1 == '\r' || *lpsz1 == '\n') {
*lpsz1 = '\0';
} else {
lpsz1 = CharNext(lpsz1);
}
}
if (_stricmp(lpsz, lpCmdString)==0) {
MHFree(lpCmdString);
fExecNext = TRUE;
}
}
MHFree(lpsz);
if (fReply && LppdCur) {
OSDInfoReply(LppdCur->hpid, LptdCur->htid, NULL, 0);
}
break;
case dbcInfoReq:
bfRefresh = UPDATE_NONE;
fExecNext = TRUE;
if (!GetQueueItemQuad()) { /* fUniCode */
// ANSI
lpsz = (LPSTR) GetQueueItemQuad(); /* String */
} else {
// Unicode
LPWSTR lpw;
lpw = (LPWSTR) GetQueueItemQuad(); /* String */
l = WideCharToMultiByte(CP_ACP,
0,
lpw,
-1,
NULL,
0,
NULL,
NULL
);
lpsz = (PSTR) MHAlloc(l+1);
Assert(lpsz);
WideCharToMultiByte(CP_ACP,
0,
lpw,
-1,
lpsz,
l+1,
NULL,
NULL
);
MHFree(lpw);
}
CmdGetDefaultPrompt(szOldPrompt);
CmdSetDefaultPrompt(lpsz);
CmdSetCmdProc(CmdHandleInputString, CmdExecutePrompt);
CmdDoPrompt(TRUE, TRUE);
FCmdDoingInput = TRUE;
MHFree(lpsz);
break;
case dbcException: /* DBG_REFRESH */
{
EXCEPTION_LIST *eList;
LPEPR lpepr;
lptd = (LPTD) GetQueueItemQuad();
if (lptd != 0) {
lppd = lptd->lppd;
htid = lptd->htid;
hpid = lppd->hpid;
} else {
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
lppd = LppdOfHpid(hpid);
Assert(lppd);
lptd = LptdOfLppdHtid(lppd, htid);
}
LppdCur = lppd;
LptdCur = lptd;
lpepr = (LPEPR) GetQueueItemQuad();
Assert(lpepr);
if (LppdCur->pstate == psPreRunning) {
/*
* We hit an exception before the process
* had finished loading. We probably won't ever
* hit the loader BP, so mark this as loaded now.
*/
LppdCur->pstate = psStopped;
BPTResolveAll(LppdCur->hpid,TRUE);
SetProcessExceptions(LppdCur);
#ifdef SHOW_MAGIC
CmdLogFmt("(Exception caught while loading)\r\n");
#endif
}
for ( eList = LppdCur->exceptionList;
eList;
eList = eList->next )
{
if ( eList->dwExceptionCode == lpepr->ExceptionCode ) {
break;
}
}
// The local list should always match the DM's list
// except on second chance, when the DM always punts
Assert(eList == 0 ||
lpepr->efd == eList->efd ||
(!lpepr->dwFirstChance &&
(lpepr->efd == efdStop || lpepr->efd == efdCommand)));
if (lpepr->dwFirstChance) {
SetPTState(psInvalidState, tsException1);
} else {
SetPTState(psInvalidState, tsException2);
}
AuxPrintf(1, "Exception %d, FirstChance == %d, efd == %d",
lpepr->ExceptionCode,
lpepr->dwFirstChance,
lpepr->efd);
switch (lpepr->efd) {
case efdNotify:
SetPTState(psInvalidState, tsRunning);
case efdStop:
case efdCommand:
CmdLogVar((WORD)((lpepr->dwFirstChance)?
DBG_Exception1_Occurred : DBG_Exception2_Occurred),
lpepr->ExceptionCode,
(eList == NULL || eList->lpName == NULL) ?
"Unknown" : eList->lpName);
if (lpepr->efd == efdNotify) {
bfRefresh = UPDATE_NONE;
} else {
fGetFocus = TRUE;
lptd->lppd->fStopAtEntry = FALSE;
if ( eList ) {
if ( lpepr->dwFirstChance && eList->lpCmd ) {
CmdPrependCommands(lptd, eList->lpCmd);
} else if ( !lpepr->dwFirstChance && eList->lpCmd2 ) {
CmdPrependCommands(lptd, eList->lpCmd2);
}
}
if (!DoStopEvent(lptd)) {
CmdLogVar(DBG_Thread_Stopped);
}
}
break;
case efdIgnore:
bfRefresh = UPDATE_NONE;
CmdLogFmt("WINDBG: benign error - \r\n efdIgnore should never reach the shell - \r\n");
CmdLogFmt(" ExceptionCode == %08x\r\n", lpepr->ExceptionCode);
break;
default:
// undefined efd value
bfRefresh = UPDATE_NONE;
CmdLogFmt("INTERNAL ERROR: unrecognized efd %d\r\n", lpepr->efd);
Assert(FALSE);
break;
}
MHFree(lpepr);
break;
}
/*
* This message is recieved when a function call from the
* expression evaluator is finished.
* DO NOTHING
*/
case dbcExecuteDone: /* DBG_INFO */
bfRefresh = UPDATE_NONE;
fRecurse = FALSE;
fExecNext = FALSE;
break;
case dbcServiceDone:
// Some Ioctl has finished executing
// Signal the synchronization event to release the command window
GetQueueItemQuad();
SetEvent( hEventIoctl );
bfRefresh = UPDATE_NONE;
fExecNext = FALSE;
break;
case dbcCanStep:
CmdLogFmt("WINDBG: dbcCanStep is obsolete\r\n");
RAssert(FALSE);
break;
case (DBC)dbcRemoteQuit:
bfRefresh = UPDATE_NONE;
CmdLogFmt("Connection to remote has been broken\r\n" );
CmdLogFmt("Stopped debugging\r\n" );
DisconnectDebuggee();
fRecurse = FALSE;
return bfRefresh;
case (DBC)dbcMemoryChanged:
bfRefresh = UPDATE_NONE;
BPUpdateMemory( (ULONG) GetQueueItemQuad() );
fExecNext = FALSE;
bfRefresh = UPDATE_NONE;
break;
case dbcSegLoad:
lptd = (LPTD)GetQueueItemQuad();
Assert(lptd != NULL);
lppd = lptd->lppd;
LppdCur = lppd;
LptdCur = lptd;
bfRefresh = UPDATE_NONE;
fExecNext = FALSE;
SHChangeProcess(lppd->hpds);
BPSegLoad( (ULONG)GetQueueItemQuad() );
Go();
break;
case dbcExitedFunction:
lptd = (LPTD) GetQueueItemQuad();
if (lptd == NULL) {
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
}
// return value:
dw = (DWORD) GetQueueItemQuad();
fExecNext = FALSE;
bfRefresh = UPDATE_NONE;
break;
case (DBC)dbcCommError:
default:
lptd = (LPTD) GetQueueItemQuad();
if (lptd == NULL) {
hpid = (HPID)GetQueueItemQuad();
htid = (HTID)GetQueueItemQuad();
}
CmdLogFmt("WINDBG: Unknown DBC %x\r\n", dbcCallback);
fExecNext = FALSE;
break;
}
// Thread and/or process may have been changed - try to make
// sure that both are valid, and update status line.
if (LptdCur != NULL) {
LppdCur = LptdCur->lppd;
} else if (LppdCur != NULL) {
LptdCur = LppdCur->lptdList;
}
if (LptdCur == NULL) {
GetFirstValidPDTD(&LppdCur, &LptdCur);
}
if (LppdCur == NULL) {
AuxPrintf(1, "(CmdExecNext: There are no processes)");
} else if (LptdCur == NULL) {
AuxPrintf(1, "(CmdExecNext: There are no threads)");
} else if (LppdCur != LppdT || LptdCur != LptdT) {
// Any time we update the context we must invalidate the
// Watch window.
bfRefresh |= UPDATE_CONTEXT |
UPDATE_ALLDBGWIN |
UPDATE_SYMBOLS_CHANGED;
}
// update status line
SetPidTid_StatusBar(LppdCur, LptdCur);
// Update the users view of the world to reflect the last debug
// event.
if ((bfRefresh != UPDATE_NONE) &&
(LptdCur != NULL) && (!LptdCur->fInFuncEval)) {
UpdateDebuggerState(bfRefresh);
}
// Worry about executing the next command on the list
if (fExecNext && (LptdCur == NULL || LptdCur->fInFuncEval == FALSE) ) {
if (CmdExecuteLine(NULL)) {
if ((AutoRun == arSource || AutoRun == arCmdline)
&& !FAutoRunSuppress ) {
PostMessage(Views[cmdView].hwndClient, WU_AUTORUN, 0, 0);
} else {
CmdDoPrompt(!FCmdDoingInput, FALSE);
}
} else {
fGetFocus = FALSE;
}
}
// Grab focus if we really stopped somewhere.
if (fGetFocus) {
HEXE hexe;
ADDR Addr;
if (LppdCur && LptdCur) {
OSDGetAddr(LppdCur->hpid, LptdCur->htid, adrPC, &Addr);
SHChangeProcess(LppdCur->hpds);
if ( (HPID)emiAddr( Addr ) == LppdCur->hpid ) {
// Get right EMI and load symbols if defered.
emiAddr( Addr ) = 0;
OSDSetEmi(LppdCur->hpid,LptdCur->htid,&Addr);
}
hexe = (HEXE)emiAddr( Addr );
if ( hexe && (HPID)hexe != LppdCur->hpid ) {
SHWantSymbols( hexe );
}
}
if (!RemoteRunning) {
EnsureFocusDebugger();
}
}
}
} __except(GenericExceptionFilter(GetExceptionInformation())) {
//DAssert(FALSE);
}
fRecurse = FALSE;
return(bfRefresh);
} /* CmdExecNext() */
BOOL
GetAutoRunSuppress(
void
)
{
return FAutoRunSuppress;
}
BOOL
SetAutoRunSuppress(
BOOL f
)
{
BOOL ff = FAutoRunSuppress;
FAutoRunSuppress = f;
return ff;
}
/*
Hotkey handler
^C is used for a general interrupt signal. When a ^C is caught,
a list of handlers is walked. Each handler is executed, and the return
value checked. If the return value is TRUE, the walk continues. If
the value is FALSE, the walk is terminated.
The list is walked in MRU order; the most recently registered handler
is called first.
*/
PCTRLC_HANDLER
AddCtrlCHandler(
CTRLC_HANDLER_PROC pfnFunc,
DWORD dwParam
)
/*++
Routine Description:
Add a CtrlC handler routine to the handler list.
Arguments:
pfnFunc - Supplies pointer to handler function
dwParam - Supplies parameter to pass to function
Return Value:
A pointer to the registered handler. This is only used
for removing the handler from the chain.
--*/
{
PCTRLC_HANDLER pcch = (PCTRLC_HANDLER) malloc(sizeof(CTRLC_HANDLER));
if (pcch) {
pcch->pfnFunc = pfnFunc;
pcch->dwParam = dwParam;
pcch->next = PcchHead;
PcchHead = pcch;
}
return pcch;
}
BOOL
RemoveCtrlCHandler(
PCTRLC_HANDLER pcch
)
/*++
Routine Description:
Remove a CtrlC handler from the list. This can remove a specific
handler, or remove the last one added.
Arguments:
pcch - Supplies pointer to the handler to remove. If this is
NULL, the last one added will be removed.
Return Value:
TRUE if the handler was removed, FALSE if it did not exist.
--*/
{
PCTRLC_HANDLER *ppcch;
if (!pcch) {
pcch = PcchHead;
}
for ( ppcch = &PcchHead; *ppcch; ppcch = &((*ppcch)->next) ) {
if (*ppcch == pcch) {
*ppcch = pcch->next;
free(pcch);
return TRUE;
}
}
return FALSE;
}
VOID
DispatchCtrlCEvent(
VOID
)
/*++
Routine Description:
Walk the list of Ctrl C handlers, calling each one until one
returns FALSE.
Arguments:
None
Return Value:
None
--*/
{
PCTRLC_HANDLER pcch;
for (pcch = PcchHead; pcch; pcch = pcch->next) {
if (!(*pcch->pfnFunc)(pcch->dwParam)) {
break;
}
}
}
/*
*/
BOOL
DoCtrlCAsyncStop(
DWORD dwParam
)
{
CmdInsertInit();
CmdLogFmt("Ctrl+C <process stopping...>\r\n");
AsyncStop();
return dwParam;
}
BOOL
PolledCtrlCHandler(
DWORD dwParam
)
{
CmdInsertInit();
CmdLogFmt("^C\r\n");
fCtrlCPressed = TRUE;
return dwParam;
}
void
SetCtrlCTrap(
void
)
/*++
Routine Description:
This manages a polled Ctrl C trap. Add a handler which sets
a flag.
Arguments:
None
Return Value:
None
--*/
{
fCtrlCPressed = FALSE;
pPolledCtrlCHandler = AddCtrlCHandler(PolledCtrlCHandler, FALSE);
}
void
ClearCtrlCTrap(
void
)
/*++
Routine Description:
Clear trap for polled CTRL-C
Arguments:
None
Return Value:
None
--*/
{
if (pPolledCtrlCHandler) {
RemoveCtrlCHandler(pPolledCtrlCHandler);
pPolledCtrlCHandler = NULL;
}
fCtrlCPressed = FALSE;
}
ULONG
WDBGAPI
CheckCtrlCTrap(
void
)
/*++
Routine Description:
Allow any hotkey events to be handled, then check the POLLED Ctrl C
flag. If the Ctrl C polling handler is not in use, this will return
FALSE and not drain the message queue.
Arguments:
None
Return Value:
TRUE if CTRL-C was pressed since last checked.
--*/
{
BOOL f;
MSG msg;
BOOL fIgnore;
if (!pPolledCtrlCHandler) {
return FALSE;
}
if (cmdView == -1) {
return FALSE;
}
//while (PeekMessage(&msg, NULL, WM_HOTKEY, WM_HOTKEY, PM_REMOVE))
while (PeekMessage(&msg, Views[cmdView].hwndClient, WM_KEYDOWN, WM_DEADCHAR, PM_REMOVE)
|| PeekMessage(&msg, Views[cmdView].hwndClient, WM_COMMAND, WM_COMMAND, PM_REMOVE))
{
switch (msg.message) {
default:
fIgnore = FALSE;
break;
case WM_KEYDOWN:
case WM_KEYUP:
switch (msg.wParam) {
default:
fIgnore = TRUE;
break;
case 'C':
case VK_CONTROL:
case VK_RCONTROL:
case VK_LCONTROL:
fIgnore = FALSE;
break;
}
break;
case WM_CHAR:
switch (msg.wParam) {
default:
fIgnore = TRUE;
break;
case 'C':
case VK_CONTROL:
case VK_RCONTROL:
case VK_LCONTROL:
fIgnore = FALSE;
break;
}
break;
}
if (!fIgnore) {
ProcessQCQPMessage(&msg);
}
}
f = fCtrlCPressed;
fCtrlCPressed = FALSE;
return f;
}
BOOL
CmdSetEatEOLWhitespace(
BOOL ff
)
/*++
Routine Description:
Set value of EatEOLWhitespace flag to control behaviour of
data entry in command window.
Arguments:
ff - Supplies new value for fEatEOLWhitespace
Return Value:
Old value of fEatEOLWhitespace
--*/
{
BOOL f = fEatEOLWhitespace;
fEatEOLWhitespace = ff;
return f;
}
void
CmdSetAutoHistOK(
BOOL f
)
{
FAutoHistOK = f;
}
BOOL
CmdGetAutoHistOK(
void
)
{
return FAutoHistOK;
}
LRESULT
CmdEditProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
This function is the window message processor for the command
window class. It processes those messages which are of interest
to this specific window class and passes all other messages on to
the default MDI window procedure handler
Arguments:
hwnd - Supplies window handle to the command window
msg - Supplies message to be processed
wParam - Supplies info about the message
lParam - Supplies info about the message
Return Value:
various
--*/
{
int i;
int x;
LRESULT lRet = 0;
int XPos, YPos;
int Xro, Yro;
BOOL fShift;
BOOL fCtrl;
long first;
LPLINEREC pl;
LPBLOCKDEF pb;
char szStr[MAX_LINE_SIZE];
int doc = Views[cmdView].Doc;
int nLines;
BOOL fDoHist;
static BOOL fShowingLine = FALSE;
static BOOL fEdited = FALSE;
static int nHistoryLine = 0;
static int nHistBufTop = 0; // oldest command in history
static int nHistBufBot = 0; // where next one goes
static LPSTR alpszHistory[MAX_CMDWIN_HISTORY] = {0};
switch (msg) {
case WU_INITDEBUGWIN:
/*
* set up ctrlc handler
*/
AddCtrlCHandler(DoCtrlCAsyncStop, FALSE);
/*
** Initialize cmd processor, show initial prompt.
*/
CmdSetDefaultCmdProc();
if (!AutoRun) {
CmdDoPrompt(TRUE, TRUE);
GetRORegion(cmdView, &Xro, &Yro);
PosXY(cmdView, Xro, Yro, FALSE);
}
return FALSE;
case WM_SETFOCUS:
InterlockedExchangePointer((PVOID *) &hCurrAccTable, hCmdWinAccTable);
#if 0
// Under development
{
// Set the context sensitive menu
HMENU hmenuCurr = GetMenu(hwndFrame);
if (hmenuCurr != hmenuCmdWin) {
SendMessage(g_hwndMDIClient, WM_MDISETMENU, (WPARAM) hwndFrame, (LPARAM) hmenuCmdWin);
Assert(DrawMenuBar(hwndFrame));
}
}
#endif
break;
case WM_KILLFOCUS:
InterlockedExchangePointer((PVOID *) &hCurrAccTable, hMainAccTable);
break;
case WM_KEYDOWN:
fShift = (GetKeyState(VK_SHIFT) < 0);
fCtrl = (GetKeyState(VK_CONTROL) < 0);
GetRORegion(cmdView, &Xro, &Yro);
XPos = Views[cmdView].X;
YPos = Views[cmdView].Y;
nLines = max(Docs[doc].NbLines, 1);
if (YPos >= Yro && XPos >= Xro) {
if (YPos > Yro) {
Xro = 0;
}
// In writeable region
switch (wParam) {
case 'R':
if (fCtrl) {
// hack...
// this is just a macro for ".resync"
InsertBlock(doc, Xro, YPos, 7, ".resync");
InvalidateLines(cmdView, nLines-1, nLines-1, TRUE);
PosXY(cmdView, Xro + 7, nLines-1, FALSE);
lRet = 0;
break;
}
default:
fEdited = TRUE;
lRet = CallWindowProc(lpfnEditProc, hwnd, msg, wParam, lParam);
break;
case VK_UP:
lRet = 0;
if (fCtrl) {
// Execute magic ctrl-up
KeyDown(cmdView, wParam, fShift, FALSE);
} else {
// get previous history line, if any:
if (!fShowingLine) {
// up from new line; show last cmd
goto GotHistLine;
} else if (nHistoryLine != nHistBufTop) {
if (--nHistoryLine < 0) {
nHistoryLine = MAX_CMDWIN_HISTORY-1;
}
goto GotHistLine;
} /* else don't do anything */
}
lRet = 0;
break;
case VK_DOWN:
// get next history line
lRet = 0;
i = nHistoryLine;
if (i != nHistBufBot && ++i >= MAX_CMDWIN_HISTORY) {
i = 0;
}
if (i != nHistBufBot) {
nHistoryLine = i;
} else {
// no more history; forget it.
break;
}
GotHistLine:
// is history empty?
if (!alpszHistory[nHistoryLine]) {
break;
}
fShowingLine = TRUE;
fEdited = FALSE;
// erase command line...
DeleteBlock(doc, Xro, nLines - 1, MAX_USER_LINE, nLines - 1);
// insert new line:
InsertBlock(doc, Xro, nLines-1,
strlen(alpszHistory[nHistoryLine]),
alpszHistory[nHistoryLine]);
InvalidateLines(cmdView, nLines-1, nLines-1, TRUE);
PosXY(cmdView, Xro + strlen(alpszHistory[nHistoryLine]), nLines-1, FALSE);
break;
case VK_LEFT:
lRet = 0;
if (XPos > Xro) {
lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam );
} else if (fCtrl) {
KeyDown(cmdView, wParam, fShift, FALSE);
}
break;
case VK_BACK:
lRet = 0;
if (XPos > Xro) {
fEdited = TRUE;
lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam );
}
break;
case VK_HOME:
if (fCtrl && fShift || fCtrl) {
// Ctrl+Shift+Home or Ctrl+Home pressed
lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam );
}if (fShift) {
// Shift+Home pressed
// The farthest position to the left that can be selected is 2
int tmp_int = FirstNonBlank(doc, YPos);
int X = max(tmp_int, 2);
if (XPos != X) {
SelPosXY(cmdView, X, YPos);
} else {
SelPosXY(cmdView, 2, YPos);
}
} else {
// Home pressed
PosXY(cmdView, Xro, nLines-1, FALSE);
}
break;
case VK_ESCAPE:
// erase command line...
DeleteBlock(doc, Xro, nLines - 1, MAX_USER_LINE, nLines - 1);
PosXY(cmdView, Xro, nLines - 1, FALSE);
InvalidateLines(cmdView, nLines-1, nLines-1, TRUE);
fShowingLine = FALSE;
fEdited = TRUE;
break;
case VK_RETURN:
first = YPos;
FirstLine(doc, &pl, &first, &pb);
if (!fEatEOLWhitespace) {
x = pl->Length - LHD;
} else {
// put cursor after last non-white char on line
// ExpandTabs() expands the string into global
// el[] and remembers the length in elLen.
ExpandTabs(&pl);
x = elLen-1;
while (x > -1) {
if (isspace(el[x])) {
--x;
} else {
break;
}
}
x++;
}
x = max(x, Xro);
if (!g_contGlobalPreferences_WkSp.m_bCommandRepeat || !CmdGetAutoHistOK() || x != Xro) {
PosXY(cmdView, x, YPos, FALSE);
fDoHist = TRUE;
} else {
fDoHist = FALSE;
if (!alpszHistory[nHistoryLine]) {
*szStr = 0;
} else {
strcpy(szStr, alpszHistory[nHistoryLine]);
if (tolower(*szStr) == 'g' || tolower(*szStr) == '.' ||
tolower(*szStr) == 'l') {
*szStr = 0;
} else {
InsertBlock(doc, Xro, YPos,
strlen(alpszHistory[nHistoryLine]),
alpszHistory[nHistoryLine]);
InvalidateLines(cmdView, nLines-1, nLines-1, TRUE);
PosXY(cmdView,
Xro + strlen(alpszHistory[nHistoryLine]),
YPos,
FALSE);
}
}
}
// give user visual feedback
SetCaret(cmdView, 0, YPos, -1);
UpdateWindow(hwnd);
// give CR to edit mangler
lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam );
// the line we just entered (reload it, it may have been mangled):
first = YPos;
FirstLine(doc, &pl, &first, &pb);
if (fDoHist) {
strncpy(szStr, pl->Text + Xro, x - Xro);
szStr[x - Xro] = 0;
// remember history
// this version remembers everything in order,
// but still only resets the history line when
// it has been edited.
if (*szStr) {
if (alpszHistory[nHistBufBot]) {
Assert(nHistBufBot == nHistBufTop);
free(alpszHistory[nHistBufTop++]);
if (nHistBufTop >= MAX_CMDWIN_HISTORY) {
nHistBufTop = 0;
}
}
if (!fShowingLine || fEdited) {
nHistoryLine = nHistBufBot;
}
alpszHistory[nHistBufBot++] = _strdup(szStr);
if (nHistBufBot >= MAX_CMDWIN_HISTORY) {
nHistBufBot = 0;
}
}
}
fShowingLine = FALSE;
CmdFileString(szStr);
CmdFileString("\r\n");
SendClientOutput(szStr, strlen(szStr));
SendClientOutput("\r\n", 2);
CmdDoLine(szStr);
CmdDoPrompt(TRUE, TRUE);
lRet = 0;
break;
}
} else {
// in readonly region
// everything has to work here... the edit mangler will protect
// the readonly region, so we can just throw everything at it, but
// we need to decide when to fall onto the command line, and when
// to leave things alone...
first = nLines - 1;
FirstLine(doc, &pl, &first, &pb);
if (wParam == CTRL_M) {
// position at end of cmd line
ClearSelection(cmdView);
PosXY(cmdView, pl->Length-LHD, nLines - 1, FALSE);
lRet = 0;
} else {
EnableReadOnlyBeep(FALSE);
lRet = CallWindowProc(lpfnEditProc, hwnd, msg, wParam, lParam);
if (QueryReadOnlyError()) {
// position at end of command line, and try again
ClearSelection(cmdView);
PosXY(cmdView, pl->Length-LHD, nLines - 1, FALSE);
lRet = CallWindowProc(lpfnEditProc, hwnd, msg, wParam,
lParam);
}
EnableReadOnlyBeep(TRUE);
}
}
return lRet;
case WM_IME_REPORT:
if (IR_STRING != wParam) {
break;
}
// Fall through
case WM_CHAR:
// the interesting cases have already been handled; we just want
// to fall onto the command line in case of an error
if (wParam == CTRL_R) {
return 0;
}
EnableReadOnlyBeep(FALSE);
lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam );
if (QueryReadOnlyError()) {
// position at end of command line, and try again
ClearSelection(cmdView);
nLines = max(Docs[doc].NbLines, 1);
first = nLines - 1;
FirstLine(doc, &pl, &first, &pb);
PosXY(cmdView, pl->Length-LHD, nLines - 1, FALSE);
lRet = CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam );
}
EnableReadOnlyBeep(TRUE);
return lRet;
case WM_PASTE:
if (OpenClipboard(hwndFrame)) {
HANDLE hData;
size_t size;
LPSTR p1;
LPSTR p;
hData = GetClipboardData(CF_TEXT);
if (hData && (size = GlobalSize (hData))) {
if (size >= MAX_CLIPBOARD_SIZE) {
ErrorBox(ERR_Clipboard_Overflow);
} else if ( p = (PSTR) GlobalLock(hData) ) {
int x, y;
x = Views[cmdView].X;
y = Views[cmdView].Y;
p1 = p;
while (size && *p1) {
size--;
if (IsDBCSLeadByte(*p1) && *(p1+1)) {
p1 += 2;
size--;
continue;
}
if (*p1 == '\r' || *p1 == '\n') {
break;
}
p1++;
}
size = (size_t) (p1 - p);
InsertStream(cmdView, x, y, size, p, TRUE);
PosXY(cmdView, x + size, y, TRUE);
DbgX(GlobalUnlock (hData) == FALSE);
}
CloseClipboard();
}
}
return 0;
case WM_DESTROY:
/*
** Destroy this instance of the window proc
*/
//UnregisterHotKey(NULL, IDH_CTRLC);
UnregisterHotKey(hwnd, IDH_CTRLC);
while (RemoveCtrlCHandler(NULL)) {
;
}
FreeProcInstance((WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC,
(DWORD_PTR)lpfnEditProc));
break;
case WU_AUTORUN:
/*
** Need to get and process an auto-run command
*/
CmdAutoRunNext();
break;
case WU_LOG_REMOTE_CMD:
// Echo and handle a command sent by a remote client
if (wParam) {
CmdLogFmtEx( TRUE, FALSE, TRUE, (LPSTR)lParam);
}
CmdDoLine( (LPSTR)lParam );
CmdDoPrompt(!FCmdDoingInput, TRUE);
FCmdDoingInput = FALSE;
if (wParam) {
free((LPVOID)lParam);
}
break;
case WU_LOG_REMOTE_MSG:
// print some random junk from a remote client
CmdInsertInit();
if (!wParam) {
GetRORegion(cmdView, &Xro, &Yro);
first = Yro;
FirstLine(doc, &pl, &first, &pb);
strncpy(szStr, pl->Text, Xro);
szStr[Xro] = 0;
CmdLogDebugString(szStr, FALSE);
}
CmdLogDebugString( (LPSTR)lParam, TRUE);
free( (LPSTR)lParam );
break;
}
return ( CallWindowProc( lpfnEditProc, hwnd, msg, wParam, lParam ) );
} /* CmdEditProc() */