/*++ Copyright (c) 2000 Microsoft Corporation Module Name: output.cxx Abstract: This file contains output state control and output callback classes. Author: Jason Hartman (JasonHa) 2000-11-20 Environment: User Mode --*/ #include "precomp.hxx" HRESULT OutputControl::SetControl( ULONG OutputControl, PDEBUG_CLIENT Client ) { ULONG SendMask = OutputControl & DEBUG_OUTCTL_SEND_MASK; if (OutputControl != DEBUG_OUTCTL_AMBIENT && ( #if DEBUG_OUTCTL_THIS_CLIENT > 0 SendMask < DEBUG_OUTCTL_THIS_CLIENT || #endif SendMask > DEBUG_OUTCTL_LOG_ONLY || (OutputControl & ~(DEBUG_OUTCTL_SEND_MASK | DEBUG_OUTCTL_NOT_LOGGED | DEBUG_OUTCTL_OVERRIDE_MASK)))) { return E_INVALIDARG; } if (Client != NULL) { HRESULT hr; PDEBUG_CONTROL NewControl; // Switch to new client if ((hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&NewControl)) != S_OK) { return hr; } if (Control != NULL) Control->Release(); Control = NewControl; } OutCtl = OutputControl; return S_OK; } HRESULT OutputControl::Output( ULONG Mask, PCSTR Format, ... ) { HRESULT hr; va_list Args; if (Control == NULL) return E_FAIL; va_start(Args, Format); if (OutCtl == DEBUG_OUTCTL_AMBIENT) { hr = Control->OutputVaList(Mask, Format, Args); } else { hr = Control->ControlledOutputVaList(OutCtl, Mask, Format, Args); } va_end(Args); return hr; } HRESULT OutputControl::OutputVaList( ULONG Mask, PCSTR Format, va_list Args ) { HRESULT hr; if (Control == NULL) return E_FAIL; if (OutCtl == DEBUG_OUTCTL_AMBIENT) { hr = Control->OutputVaList(Mask, Format, Args); } else { hr = Control->ControlledOutputVaList(OutCtl, Mask, Format, Args); } return hr; } HRESULT OutputControl::Output( PCSTR Format, ... ) { HRESULT hr; va_list Args; va_start(Args, Format); hr = OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args); va_end(Args); return hr; } HRESULT OutputControl::OutErr( PCSTR Format, ... ) { HRESULT hr; va_list Args; va_start(Args, Format); hr = OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args); va_end(Args); return hr; } HRESULT OutputControl::OutWarn( PCSTR Format, ... ) { HRESULT hr; va_list Args; va_start(Args, Format); hr = OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args); va_end(Args); return hr; } HRESULT OutputControl::OutVerb( PCSTR Format, ... ) { HRESULT hr; va_list Args; va_start(Args, Format); hr = OutputVaList(DEBUG_OUTPUT_VERBOSE, Format, Args); va_end(Args); return hr; } HRESULT OutputControl::GetInterrupt( ) { return (Control == NULL) ? E_FAIL : Control->GetInterrupt(); } HRESULT OutputControl::SetInterrupt( ULONG Flags ) { return (Control == NULL) ? E_FAIL : Control->SetInterrupt(Flags); } HRESULT OutputControl::Evaluate( IN PCSTR Expression, IN ULONG DesiredType, OUT PDEBUG_VALUE Value, OUT OPTIONAL PULONG RemainderIndex ) { return (Control == NULL) ? E_FAIL : Control->Evaluate(Expression, DesiredType, Value, RemainderIndex); } HRESULT OutputControl::CoerceValue( IN PDEBUG_VALUE In, IN ULONG OutType, OUT PDEBUG_VALUE Out ) { return (Control == NULL) ? E_FAIL : Control->CoerceValue(In, OutType, Out); } HRESULT OutputControl::IsPointer64Bit( ) { return (Control == NULL) ? E_FAIL : Control->IsPointer64Bit(); } OutputState::OutputState( PDEBUG_CLIENT OrgClient, BOOL SameClient ) { hrInit = S_FALSE; Client = NULL; Control = NULL; Symbols = NULL; SetCallbacks = FALSE; CreatedClient = FALSE; Saved = FALSE; if (OrgClient != NULL) { if (SameClient) { Client = OrgClient; Client->AddRef(); CreatedClient = TRUE; hrInit = S_OK; } else { hrInit = OrgClient->CreateClient(&Client); } } } OutputState::~OutputState() { if (!CreatedClient) Restore(); EXT_RELEASE(Symbols); EXT_RELEASE(Control); // If Client was newly created for OutputState, then // there shouldn't be any other references to Client. if (CreatedClient) { ULONG RemainingRefs; RemainingRefs = Client->AddRef(); if (RemainingRefs > 2) { DbgPrint("OutputState: %lu refs outstanding on created client.\n", RemainingRefs-2); DbgBreakPoint(); // As a precaution, Restore the callbacks; // so, any set callback may be cleaned up. Restore(); } Client->Release(); } if (Client != NULL) Client->Release(); } HRESULT OutputState::Setup( ULONG OutMask, PDEBUG_OUTPUT_CALLBACKS OutCallbacks ) { HRESULT hr = hrInit; ULONG LastOutMask; if (hr == S_OK) { if (CreatedClient && !Saved) { if ((hr = Client->GetOutputMask(&OrgOutMask)) == S_OK && (hr = Client->GetOutputCallbacks(&OrgOutCallbacks)) == S_OK) { Saved = TRUE; } } if (hr == S_OK && (hr = Client->GetOutputMask(&LastOutMask)) == S_OK && (hr = Client->SetOutputMask(OutMask)) == S_OK) { if (!Saved && !SetCallbacks) { OrgOutMask = LastOutMask; OrgOutCallbacks = NULL; } if ((hr = Client->SetOutputCallbacks(OutCallbacks)) == S_OK) { SetCallbacks = TRUE; } else { Client->SetOutputMask(LastOutMask); } } } if (hr == S_OK && Symbols == NULL) { hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols); } return hr; } HRESULT OutputState::Execute( PCSTR pszCommand ) { HRESULT hr = hrInit; if (hr == S_OK) { if (Control == NULL) { hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&Control); } if (hr == S_OK) { hr = Control->Execute(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED | DEBUG_OUTCTL_OVERRIDE_MASK, pszCommand, DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT); if (hr != S_OK) { DbgPrint("IDebugControl::Execute returned %s.\n", pszHRESULT(hr)); } } } return hr; } HRESULT OutputState::OutputType( IN BOOL Physical, IN ULONG64 Offset, IN PCSTR Type, IN ULONG Flags ) { HRESULT hr = hrInit; if (hr == S_OK) { if (Symbols == NULL) { hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols); } if (hr == S_OK) { ULONG64 Module; ULONG TypeId; hr = GetTypeId(Client, Type, &TypeId, &Module); if (hr != S_OK) { OutputControl OutCtl(Client); ULONG ModuleIndex = 0; while ((hr = Symbols->GetModuleByIndex(ModuleIndex, &Module)) == S_OK && (Module != 0)) { if ((hr = Symbols->GetTypeId(Module, Type, &TypeId)) == S_OK) { OutCtl.OutVerb("Found %s: TypeId 0x%lx in module @ %p.\n", Type, TypeId, Module); break; } ModuleIndex++; Module = 0; } if (hr == S_OK && (Module == 0 || TypeId == 0)) { hr = S_FALSE; } if (hr != S_OK) { OutCtl.OutVerb("Couldn't find %s in any of %lu modules.\n", Type, ModuleIndex); } } if (hr == S_OK) { hr = OutputType(Physical, Offset, Module, TypeId, Flags); } } } return hr; } HRESULT OutputState::OutputType( IN BOOL Physical, IN ULONG64 Offset, IN ULONG64 Module, IN ULONG TypeId, IN ULONG Flags ) { HRESULT hr = hrInit; if (hr == S_OK) { if (Symbols == NULL) { hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols); } if (hr == S_OK) { if (Physical) { hr = Symbols->OutputTypedDataPhysical(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED | DEBUG_OUTCTL_OVERRIDE_MASK, Offset, Module, TypeId, Flags); } else { hr = Symbols->OutputTypedDataVirtual(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED | DEBUG_OUTCTL_OVERRIDE_MASK, Offset, Module, TypeId, Flags); } if (hr != S_OK) { DbgPrint("IDebugSymbols::OutputTypedData%s returned %s for 0x%I64x.\n", (Physical ? "Physical" : "Virtual"), pszHRESULT(hr), Offset); } } } return hr; } void OutputState::Restore() { if (SetCallbacks) { Client->SetOutputCallbacks(OrgOutCallbacks); Client->SetOutputMask(OrgOutMask); SetCallbacks = FALSE; } if (Saved) { if (OrgOutCallbacks != NULL) OrgOutCallbacks->Release(); Saved = FALSE; } } //---------------------------------------------------------------------------- // // Default output callbacks implementation, provides IUnknown for // static classes. // //---------------------------------------------------------------------------- STDMETHODIMP DefOutputCallbacks::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface ) { *Interface = NULL; if (//(InterfaceId == IID_IUnknown) || (InterfaceId == __uuidof(IDebugOutputCallbacks))) { *Interface = (IDebugOutputCallbacks *)this; AddRef(); return S_OK; } else { return E_NOINTERFACE; } } STDMETHODIMP_(ULONG) DefOutputCallbacks::AddRef( THIS ) { // This class is designed to be allocated on a // stack or statically, but we retain a refcount // for debugging purposes. return ++RefCount; } STDMETHODIMP_(ULONG) DefOutputCallbacks::Release( THIS ) { // This class is designed to be allocated on a // stack or statically, but we retain a refcount // for debugging purposes. RefCount--; if (RefCount < 1) { DbgPrint("DefOutputCallbacks@0x%p::RefCount(%lu) < 1.\n", this, RefCount); DbgBreakPoint(); } return RefCount; } STDMETHODIMP DefOutputCallbacks::Output( THIS_ IN ULONG Mask, IN PCSTR Text ) { // The default Output ignores all Output calls. return S_OK; } //---------------------------------------------------------------------------- // // DebugOutputCallbacks::Output // //---------------------------------------------------------------------------- STDMETHODIMP DebugOutputCallbacks::Output( THIS_ IN ULONG Mask, IN PCSTR Text ) { DbgPrint("Mask: 0x%lx\tOutput Begin:\n%s:Output End\n", Mask, Text); return S_OK; } //---------------------------------------------------------------------------- // // OutputReader // // General DebugOutputCallback class to parse output. // //---------------------------------------------------------------------------- STDMETHODIMP OutputReader::Output( THIS_ IN ULONG Mask, IN PCSTR Text ) { ULONG TextLen = strlen(Text); if (BufferLeft < TextLen) { PSTR NewBuffer; SIZE_T NewBufferSize; if (hHeap == NULL) { hHeap = GetProcessHeap(); if (hHeap == NULL) return S_FALSE; } // New length we need plus some extra space NewBufferSize = BufferSize + TextLen + 256; NewBuffer = (PSTR) ((Buffer == NULL) ? HeapAlloc(hHeap, 0, NewBufferSize): HeapReAlloc(hHeap, 0, Buffer, NewBufferSize)); if (NewBuffer == NULL) { DbgPrint("Buffer alloc failed.\n"); return E_OUTOFMEMORY; } // How much was really allocated? NewBufferSize = HeapSize(hHeap, 0, NewBuffer); // If this was the first alloc, initialize // buffer and account for terminating zero. if (Buffer == NULL) { NewBuffer[0] = '\0'; BufferLeft = -1; } // Update buffer data Buffer = NewBuffer; BufferLeft += NewBufferSize - BufferSize; BufferSize = NewBufferSize; } // Append new text strcat(Buffer, Text); BufferLeft -= TextLen; return S_OK; } // Discard any text left unused by Parse void OutputReader::DiscardOutput() { if (Buffer != NULL) { Buffer[0] = '\0'; BufferLeft = BufferSize - 1; } } // Get a copy of the output buffer HRESULT OutputReader::GetOutputCopy( PSTR *Copy ) { if (Copy == NULL) return E_INVALIDARG; *Copy = NULL; if (Buffer == NULL) return S_OK; SIZE_T BufferLength = BufferSize - BufferLeft; *Copy = (PSTR)HeapAlloc(hHeap, 0, BufferLength); if (*Copy != NULL) { RtlCopyMemory(*Copy, Buffer, BufferLength); return S_OK; } return E_OUTOFMEMORY; } //---------------------------------------------------------------------------- // // OutputParser // // General DebugOutputCallback class to parse output. // //---------------------------------------------------------------------------- HRESULT OutputParser::ParseOutput(FLONG Flags) { HRESULT hr; ULONG TextRemainingIndex; if (Buffer == NULL) { return S_OK; } if (Flags & PARSE_OUTPUT_ALL) { UnparsedIndex = 0; } if ((hr = Parse(Buffer + UnparsedIndex, &TextRemainingIndex)) == S_OK) { UnparsedIndex += TextRemainingIndex; if (((Flags & PARSE_OUTPUT_NO_DISCARD) == 0) && (UnparsedIndex > 0)) { RtlMoveMemory(Buffer, Buffer + UnparsedIndex, BufferSize - UnparsedIndex - BufferLeft + 1); BufferLeft += UnparsedIndex; UnparsedIndex = 0; } } return hr; } void OutputParser::DiscardOutput() { OutputReader::DiscardOutput(); UnparsedIndex = 0; } //---------------------------------------------------------------------------- // // BasicOutputParser // // Basic DebugOutputCallback class to parse output looking for // string keys and subsequent values. // //---------------------------------------------------------------------------- HRESULT BasicOutputParser::LookFor( PDEBUG_VALUE Value, PCSTR Key, ULONG Type, ULONG Radix ) { if ((1 > strlen(Key)) || (strlen(Key) >= sizeof(Entries->Key)/sizeof(Entries->Key[0]))) { return E_INVALIDARG; } if (NumEntries >= MaxEntries) return E_OUTOFMEMORY; Entries[NumEntries].Value = Value; strcpy(Entries[NumEntries].Key, Key); if (Value != NULL) { Value->Type = DEBUG_VALUE_INVALID; if (Radix == PARSER_UNSPECIFIED_RADIX) { // Set Radix to Hex since value is likely to be an address // Otherwise type a deafult of decimal. Radix = (Type == DEBUG_VALUE_INT64) ? 16 : 10; } Entries[NumEntries].Type = Type; Entries[NumEntries].Radix = Radix; } NumEntries++; return S_OK; } HRESULT BasicOutputParser::Parse( IN PCSTR Text, OUT OPTIONAL PULONG RemainderIndex ) { HRESULT hr = S_OK; PCSTR pStrUnused = Text; PCSTR pStr; ULONG EvalLen; // DbgPrint("BasicOutputParser::Parse: Searching \"%s\"\n", pStrUnused); while (CurEntry < NumEntries) { pStr = strstr(pStrUnused, Entries[CurEntry].Key); if (pStr == NULL) { break; } pStr += strlen(Entries[CurEntry].Key); if (Entries[CurEntry].Value != NULL) { hr = Evaluate(Client, pStr, Entries[CurEntry].Type, Entries[CurEntry].Radix, Entries[CurEntry].Value, &EvalLen); if (hr != S_OK) { DbgPrint("Evaluate returned HRESULT 0x%lx.\n", hr); break; } // DbgPrint("BasicOutputParser::Parse: Found 0x%I64x after \"%s\".\n", Entries[CurEntry].Value->I64, Entries[CurEntry].Key); pStr += EvalLen; } CurEntry++; pStrUnused = pStr; } if (RemainderIndex != NULL) { *RemainderIndex = (ULONG)(pStrUnused - Text); } return hr; } //---------------------------------------------------------------------------- // // BitFieldParser // // DebugOutputCallback class to parse bitfield type output // //---------------------------------------------------------------------------- BitFieldParser::BitFieldParser( PDEBUG_CLIENT Client, BitFieldInfo *BFI ) : BitFieldReader(Client, 2) { if (BFI != NULL && BitFieldReader.LookFor(&BitPos, ": Pos ", DEBUG_VALUE_INT32) == S_OK && BitFieldReader.LookFor(&Bits, ", ", DEBUG_VALUE_INT32) == S_OK) { BitField = BFI; BitField->Valid = FALSE; BitField->BitPos = 0; BitField->Bits = 0; BitField->Mask = 0; } else { BitField = NULL; } } HRESULT BitFieldParser::Parse( IN PCSTR Text, OUT OPTIONAL PULONG RemainderIndex ) { PCSTR pStrUnused = Text; ULONG UnusedIndex = 0; PCSTR pStrRemaining; BitFieldInfo Field; do { if (BitFieldReader.Complete() == S_OK) { BitFieldReader.Relook(); } BitFieldReader.Parse(pStrUnused, &UnusedIndex); pStrUnused += UnusedIndex; if (BitFieldReader.Complete() != S_OK) { break; } if (!BitField->Valid) { BitField->Valid = BitField->Compose(BitPos.I32, Bits.I32); } else { // Full extent of bit fields seen so far BitField->Bits = Bits.I32 + BitPos.I32 - BitField->BitPos; // Full mask of bit fields seen so far if (Field.Compose(BitPos.I32, Bits.I32)) { BitField->Mask |= Field.Mask; } } // See if there is anything else we might want to parse. pStrRemaining = pStrUnused; while (isspace(*pStrRemaining) || *pStrRemaining == '\n') { pStrRemaining++; } } while (pStrRemaining != '\0'); if (RemainderIndex != NULL) { *RemainderIndex = (ULONG)(pStrUnused - Text); } return S_OK; } //---------------------------------------------------------------------------- // // OutputFilter // // DebugOutputCallback class to filter output // by skipping/replacing lines. // //---------------------------------------------------------------------------- STDMETHODIMP OutputFilter::Output( THIS_ IN ULONG Mask, IN PCSTR Text ) { return (Outputing) ? S_OK : OutputReader::Output(Mask, Text); } HRESULT OutputFilter::Query( PCSTR Query, PDEBUG_VALUE Value, ULONG Type, ULONG Radix ) { if (Query == NULL) return E_INVALIDARG; if (Buffer == NULL) return S_FALSE; HRESULT hr; BasicOutputParser Parser(Client, 1); if ((hr = Parser.LookFor(Value, Query, Type, Radix)) == S_OK && (hr = Parser.Parse(Buffer, NULL)) == S_OK) { hr = Parser.Complete(); } return hr; } OutputFilter::QuerySpec::QuerySpec( ULONG QueryFlags, PCSTR QueryText ) { Next = NULL; Flags = QueryFlags; QueryLen = (QueryText== NULL) ? 0 : strlen(QueryText); if (QueryLen) { Query = new CHAR[QueryLen+1]; if (Query == NULL) { QueryLen = 0; } else { strcpy(Query, QueryText); } } else { Query = NULL; } } OutputFilter::ReplacementSpec::ReplacementSpec( ULONG QueryFlags, PCSTR QueryText, PCSTR ReplacementText ) : QuerySpec(QueryFlags, QueryText) { ReplacementLen = (ReplacementText== NULL) ? 0 : strlen(ReplacementText); if (ReplacementLen) { Replacement = new CHAR[ReplacementLen+1]; if (Replacement == NULL) { ReplacementLen = 0; } else { strcpy(Replacement, ReplacementText); } } else { Replacement = NULL; } } OutputFilter::QuerySpec ** OutputFilter::FindPrior( ULONG Flags, PCSTR Query, QuerySpec **List ) { QuerySpec **Prior = List; QuerySpec *Next; for (Next = *Prior; Next != NULL; Next = Next->Next) { if (Flags > Next->Flags || (Flags == Next->Flags && strcmp(Query, Next->Query) >= 0)) { break; } Prior = &Next->Next; } return Prior; } HRESULT OutputFilter::Replace( ULONG Flags, PCSTR Query, PCSTR Replacement ) { if (Query == NULL || (Flags & OUTFILTER_REPLACE_LINE) == 0 || (Flags & OUTFILTER_REPLACE_LINE) == (OUTFILTER_REPLACE_BEFORE | OUTFILTER_REPLACE_AFTER)) { return E_INVALIDARG; } // Don't support replacing one query each time in a single line. // This is ok if there can be no further replacements on a matching line. if (((Flags & (OUTFILTER_REPLACE_ONCE | OUTFILTER_QUERY_ONE_LINE)) == (OUTFILTER_REPLACE_EVERY | OUTFILTER_QUERY_ONE_LINE)) && !(Flags & (OUTFILTER_REPLACE_AFTER | OUTFILTER_REPLACE_NEXT_LINE))) { return E_NOTIMPL; } // Set priority to level 0 if not specified. if ((Flags & OUTFILTER_REPLACE_PRIORITY(7)) == 0) { Flags |= OUTFILTER_REPLACE_PRIORITY(0); } ReplacementSpec **PriorNext; ReplacementSpec *Next; ULONG ReplacementLen; ReplacementLen = (Replacement == NULL) ? 0 : strlen(Replacement); PriorNext = (ReplacementSpec **)FindPrior(Flags, Query, (QuerySpec**)&ReplaceList); Next = *PriorNext; if (Next != NULL && Flags == Next->Flags && strcmp(Query, Next->Query) == 0) { if (ReplacementLen == 0) { if (Next->Replacement != NULL) { Next->Replacement[0] = '\0'; } } else { if (ReplacementLen > Next->ReplacementLen) { PSTR NewReplacement = new CHAR[ReplacementLen+1]; if (NewReplacement == NULL) return E_OUTOFMEMORY; if (Next->Replacement != NULL) { delete[] Next->Replacement; Next->Replacement = NewReplacement; } } strcpy(Next->Replacement, Replacement); } Next->ReplacementLen = ReplacementLen; } else { ReplacementSpec *NewQuery; NewQuery = new ReplacementSpec(Flags, Query, Replacement); if (NewQuery == NULL) return E_OUTOFMEMORY; if (NewQuery->Query == NULL || (ReplacementLen && NewQuery->Replacement == NULL)) { delete NewQuery; return E_OUTOFMEMORY; } NewQuery->Next = Next; *PriorNext = NewQuery; } return S_OK; } HRESULT OutputFilter::Skip( ULONG Flags, PCSTR Query ) { if (Query == NULL) { return E_INVALIDARG; } QuerySpec **PriorNext; QuerySpec *Next; PriorNext = FindPrior(Flags, Query, &SkipList); Next = *PriorNext; if (Next == NULL || Flags != Next->Flags || strcmp(Query, Next->Query) != 0) { QuerySpec *NewQuery; NewQuery = new QuerySpec(Flags, Query); if (NewQuery == NULL) return E_OUTOFMEMORY; if (NewQuery->Query == NULL) { delete NewQuery; return E_OUTOFMEMORY; } NewQuery->Next = Next; *PriorNext = NewQuery; } return S_OK; } OutputFilter::QuerySpec * OutputFilter::FindMatch( PCSTR Text, QuerySpec *List, ULONG Start, ULONG Flags, PULONG MatchPos ) { ULONG Remaining = strlen(Text); PCSTR Search; QuerySpec *Match; if (MatchPos != NULL) *MatchPos = 0; if (List == NULL || Text == NULL || (Remaining = strlen(Text)) <= Start) return NULL; Search = Text + Start; Remaining -= Start; do { for (Match = List; Match != NULL; Match = Match->Next) { if (Match->Flags & OUTFILTER_QUERY_ENABLED && Remaining >= Match->QueryLen && strncmp(Search, Match->Query, Match->QueryLen) == 0) { if (Match->Flags & OUTFILTER_QUERY_WHOLE_WORD) { if ((Search > Text && iscsym(Search[-1])) || iscsym(Search[Match->QueryLen])) { continue; } } /* if (Match->Flags & OUTFILTER_QUERY_ONE_LINE) { Match->Flags &= ~OUTFILTER_QUERY_ENABLED; } */ Match->Flags |= OUTFILTER_QUERY_HIT; if (MatchPos != NULL) { *MatchPos = (ULONG)(Search - Text); } return Match; } } Search++; Remaining--; } while (Remaining && !(Flags & OUTFILTER_FINDMATCH_AT_START)); return NULL; } HRESULT OutputFilter::OutputText( OutputControl *OutCtl, ULONG Mask ) { // 1. Get line of read buffer // 2. Search for a skip query match // 3. Search for any/all replace query matches if (Buffer == NULL) return S_OK; HRESULT hr; PSTR pNextLine; CHAR EndChar; PSTR pFilter; if (OutCtl == NULL) { OutCtl = new OutputControl(Client); if (OutCtl == NULL) { return E_OUTOFMEMORY; } } else { OutCtl->AddRef(); } // Can we quickly just output all text? if (SkipList == NULL && ReplaceList == NULL) { hr = OutCtl->Output(Mask, Buffer); OutCtl->Release(); return hr; } Outputing = TRUE; // Enable all queries and reset hit markings. QuerySpec *Query; for (Query = SkipList; Query != NULL; Query = Query->Next) { Query->Flags = (Query->Flags & ~OUTFILTER_QUERY_HIT) | OUTFILTER_QUERY_ENABLED; } for (Query = (QuerySpec *)ReplaceList; Query != NULL; Query = Query->Next) { Query->Flags = (Query->Flags & ~OUTFILTER_QUERY_HIT) | OUTFILTER_QUERY_ENABLED; } hr = S_OK; pNextLine = Buffer; while (hr == S_OK && *pNextLine != '\0') { if (OutCtl->GetInterrupt() == S_OK) { OutCtl->SetInterrupt(DEBUG_INTERRUPT_PASSIVE); break; } pFilter = pNextLine; // Find end of this line while ((*pNextLine != '\0') && (*pNextLine != '\n') && (*pNextLine != '\r') && (*pNextLine != '\f')) { pNextLine++; } EndChar = *pNextLine; *pNextLine = '\0'; // Search for a skip match Query = FindMatch(pFilter, SkipList); if (Query != NULL) { DbgPrint("Skipping line with %s.\n", Query->Query); if (Query->Flags & OUTFILTER_QUERY_ONE_LINE) { Query->Flags &= ~OUTFILTER_QUERY_ENABLED; } *pNextLine = EndChar; if (EndChar != '\0') { pNextLine++; } } else { PSTR pFilterLine = pFilter; ULONG MatchPos = 0; ReplacementSpec *Replace; for (Replace = ReplaceList; Replace != NULL; Replace = (ReplacementSpec *)Replace->Next) { if (!(Replace->Flags & (OUTFILTER_QUERY_ONE_LINE | OUTFILTER_QUERY_ENABLED))) { Replace->Flags |= OUTFILTER_QUERY_ENABLED; } } while (pFilter < pNextLine && (Replace = (ReplacementSpec *)FindMatch(pFilterLine, ReplaceList, MatchPos, OUTFILTER_FINDMATCH_DEFAULT, &MatchPos)) != NULL) { if (Replace->Flags & (OUTFILTER_QUERY_ONE_LINE | OUTFILTER_REPLACE_ONCE)) { Replace->Flags &= ~OUTFILTER_QUERY_ENABLED; } if (Replace->Flags & OUTFILTER_REPLACE_BEFORE) { if (Replace->ReplacementLen) { hr = OutCtl->Output(Mask, "%s", Replace->Replacement); } pFilter = pFilterLine + MatchPos; if (!(Replace->Flags & OUTFILTER_REPLACE_NEXT_LINE) && !(Replace->Flags & OUTFILTER_REPLACE_ONCE) && !(Replace->Flags & OUTFILTER_REPLACE_TO_END)) { // This replacement leaves the query text intact. // Hence this query will keep matching; so, look for // another query which will actually modify the // query text or the text following it. Replace = (ReplacementSpec *)Replace->Next; while (Replace != NULL && Replace->Flags & OUTFILTER_REPLACE_BEFORE) { Replace = (ReplacementSpec *)Replace->Next; } Replace = (ReplacementSpec *) FindMatch(pFilterLine, Replace, MatchPos, OUTFILTER_FINDMATCH_AT_START | OUTFILTER_FINDMATCH_NO_MARK); if (Replace == NULL) { // Advance MatchPos, but not filtered text. // This unfiltered text may yet be replaced. MatchPos++; continue; } } } if (!(Replace->Flags & OUTFILTER_REPLACE_BEFORE)) { CHAR TempHolder; ULONG BeginReplacePos = MatchPos; if (!(Replace->Flags & OUTFILTER_REPLACE_THIS)) { BeginReplacePos += Replace->QueryLen; } TempHolder = pFilterLine[BeginReplacePos]; pFilterLine[BeginReplacePos] = '\0'; if ((hr = OutCtl->Output(Mask, "%s", pFilter)) == S_OK && Replace->ReplacementLen) { hr = OutCtl->Output(Mask, "%s", Replace->Replacement); } pFilterLine[BeginReplacePos] = TempHolder; } if (Replace->Flags & OUTFILTER_REPLACE_AFTER) { pFilter = pNextLine; } else { if (Replace->Flags & OUTFILTER_REPLACE_THIS) { MatchPos += Replace->QueryLen; } pFilter = pFilterLine + MatchPos; } if (Replace->Flags & OUTFILTER_REPLACE_NEXT_LINE) break; } *pNextLine = EndChar; if (EndChar != '\0') { pNextLine++; // Include any following zero length lines while ((*pNextLine == '\n') || (*pNextLine == '\r') || (*pNextLine == '\f')) { pNextLine++; } EndChar = *pNextLine; *pNextLine = '\0'; } // Output remaining portion of filtered line hr = OutCtl->Output(Mask, pFilter); if (EndChar != '\0') { *pNextLine = EndChar; } } } Outputing = FALSE; OutCtl->Release(); return hr; }