/* Written By and � 2004 Amleth Ojalen (amleth@amleth.com)
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "StdAfx.h"
#include "cablogbuffer.h"
// the data bauffer is allocated in the following block sizes
#define DATABUFFER_GROWSIZE 0x1000
// the Line bauffer is allocated in the following block sizes
#define LINEBUFFER_GROWSIZE 0x1000
// the default delimiter is an end of string.
static byte LINE_DELIM = '\0';
#pragma warning(disable : 4311)
CABLogBuffer::CABLogBuffer(void)
{
// initialise local variables.
m_lDataLength = 0;
m_lDataSize = 0;
m_pDataBuffer = 0;
m_lLineLength = 0;
m_lLineSize = 0;
m_pLineBuffer = 0;
m_lLineCount = 0;
// clear the sections.
int iCounter;
for (iCounter = 0; iCounter < _MAX_SECTIONS; iCounter++)
m_LineSections[iCounter] = NULL;
m_lSectionCount = 0;
m_lSelectionStart = 0;
m_lSelectionLength = 0;
m_lLineBiggest= 0;
m_dNewColor = RGB(255,0,0);
m_LineDelim = &LINE_DELIM;
m_iLineDelimLength = 0;
}
CABLogBuffer::~CABLogBuffer(void)
{
if (m_pLineBuffer)
{
// deallocate the Line Buffer
GlobalFree((HGLOBAL)m_pLineBuffer);
m_pLineBuffer = 0;
}
if (m_pDataBuffer)
{
// deallocate the Data Buffer
GlobalFree((HGLOBAL)m_pDataBuffer);
m_pDataBuffer = 0;
}
int iCounter;
// deallocate the Line sections
for (iCounter = 0; iCounter < _MAX_SECTIONS; iCounter++)
{
if (m_LineSections[iCounter] != NULL)
{
free(m_LineSections[iCounter]);
m_LineSections[iCounter] = NULL;
}
}
m_lSectionCount = 0;
}
bool CABLogBuffer::SetSectionFadeStep(long lSection, int iStep)
{
// this will set a sections fade step.
m_LineSections[lSection]->m_iFadeStep = iStep;
return true;
}
bool CABLogBuffer::SetSelection(long lSelectionStart, long lSelectionLength)
{
// theis will set the Selection
m_lSelectionStart = lSelectionStart;
m_lSelectionLength = lSelectionLength;
return true;
}
bool CABLogBuffer::GetSelection(long *lSelectionStart, long *lSelectionLength)
{
// this will get the selection
if (lSelectionStart)
*lSelectionStart = m_lSelectionStart;
if (lSelectionLength)
*lSelectionLength = m_lSelectionLength;
return true;
}
bool CABLogBuffer::LineToggleFlag(long lLine, DWORD dFlag)
{
// this will toggle the flag dFlag in lLine
ABLogLine * pLine;
pLine = GetLine(lLine);
if ((pLine->m_dFlags & dFlag) == dFlag)
pLine->m_dFlags &= ~dFlag;
else
pLine->m_dFlags |= dFlag;
return true;
}
long CABLogBuffer::GetLineFlagCount(DWORD dFlag)
{
// this will return the number of lines that have the flag dFlag set.
int iCounter;
int iCount;
int iLineCount;
// Clear all the Tagged Lines
iCount = GetLineCount();
iLineCount = 0;
for (iCounter = 0; iCounter < iCount; iCounter++)
if ((GetLine(iCounter)->m_dFlags & dFlag) == dFlag)
iLineCount++;
return iLineCount;
}
bool CABLogBuffer::AddLineFlag(int iLine, DWORD dFlag)
{
// this will set dFlag in iLine
GetLine(iLine)->m_dFlags |= dFlag;
return true;
}
bool CABLogBuffer::RemoveLineFlags(DWORD dFlag)
{
int iCounter;
int iCount;
// this will clear the flag dFlag from all lines.
// Clear all the Tagged Lines
iCount = GetLineCount();
for (iCounter = 0; iCounter < iCount; iCounter++)
GetLine(iCounter)->m_dFlags &= ~dFlag;
return true;
}
long CABLogBuffer::FindText(CString strFind, DWORD dFlags, long *pChar)
{
// this will all the data from the string strFind
long lStart;
char cFind;
ABLogLine * pLine;
cFind = strFind.operator [](0);
if ((dFlags & FF_FINDALL) ==FF_FINDALL)
{
// search for all occurances of strFind
// clear all the tagged lines
RemoveLineFlags(LF_TAGGED);
lStart = 0;
while (lStart < m_lDataLength)
{
if (m_pDataBuffer[lStart] == cFind)
{
if (strnicmp((char *)&m_pDataBuffer[lStart], strFind, strFind.GetLength()) == 0)
{
// found strFind, Tag the line
pLine = GetLineFromOffset(lStart);
pLine->m_dFlags |= LF_TAGGED;
}
}
lStart ++;
}
// clear the selection and return
m_lSelectionStart = 0;
m_lSelectionLength = 0;
return -1; // don't move selection
}
if ((dFlags & FF_FINDFIRST) == FF_FINDFIRST)
{
// find first, find next
if ((dFlags & FF_FINDFORWARD) == FF_FINDFORWARD)
{
// search forward
if (m_lSelectionLength == 0)
// start from the start
lStart = 0;
else
// start from the previous location + 1
lStart = m_lSelectionStart + 1;
if (lStart > m_lDataLength)
// start from the begining if the last selection was at the end
lStart = 0;
while (lStart < m_lDataLength)
{
if (m_pDataBuffer[lStart] == cFind)
{
if (strnicmp((char *)&m_pDataBuffer[lStart], strFind, strFind.GetLength()) == 0)
{
// strfind has been found.
// Set the Selection.
m_lSelectionStart = lStart;
m_lSelectionLength = strFind.GetLength() -1;
pLine = GetLineFromOffset(lStart);
if (pChar)
*pChar = lStart - pLine->m_lLineOffset;
// reutrn the lin number and the char offset.
return pLine->m_lLineIndex;
}
}
lStart ++;
}
}
if ((dFlags & FF_FINDBACKWARD) == FF_FINDBACKWARD)
{
// search backward
if (m_lSelectionLength == 0)
// start from the End
lStart = m_lDataLength;
else
// start from the previous location -1
lStart = m_lSelectionStart - 1;
if (lStart < 0)
// start from the end if the last selection was at the start
lStart = m_lDataLength;
while (lStart > -1)
{
if (m_pDataBuffer[lStart] == cFind)
{
if (strnicmp((char *)&m_pDataBuffer[lStart], strFind, strFind.GetLength()) == 0)
{
// strFind has been found.
// Set the selection
m_lSelectionStart = lStart;
m_lSelectionLength = strFind.GetLength() -1;
pLine = GetLineFromOffset(lStart);
if (pChar)
*pChar = lStart - pLine->m_lLineOffset;
// reutrn the lin number and the char offset.
return pLine->m_lLineIndex;
}
}
lStart --;
}
}
}
// nothing found
return -2;
}
CABLogBuffer::ABLogLine * CABLogBuffer::GetLineFromOffset(long lOffset)
{
//this returns the line number from a given dataoffset
int iCount;
int iCounter;
ABLogLine * pLine;
iCount = GetLineCount();
for (iCounter = 0; iCounter < iCount; iCounter++)
{
pLine = GetLine(iCounter);
if ((pLine->m_lLineOffset <= lOffset) && ((pLine->m_lLineLength + pLine->m_lLineOffset) >= lOffset))
return pLine;
}
return NULL;
}
bool CABLogBuffer::HitTest(long lLine, long lOffset, bool * pbSelection, int * iFadeStep)
{
// check to see if character lLine[lOffset] is selected or is a part of a section.
bool bReturn;
bReturn = false;
if (pbSelection)
{
// we want to know if this Character is selected
long tOffset;
tOffset = GetLine(lLine)->m_lLineOffset + lOffset;
if ((tOffset >= m_lSelectionStart) && (tOffset <= m_lSelectionStart + m_lSelectionLength) && ( m_lSelectionLength> 0) )
{
// the character is selected
*pbSelection = true;
bReturn = true;
}
else
// character is not selected
*pbSelection = false;
}
if (iFadeStep)
{
// we want to know if the Character is in a Section
if (m_lSectionCount > 0)
{
int iCounter;
long tOffset;
tOffset = GetLine(lLine)->m_lLineOffset + lOffset;
// go through all the seections
for (iCounter =0; iCounter < _MAX_SECTIONS; iCounter++)
{
if (m_LineSections[iCounter])
{
if ((tOffset >=m_LineSections[iCounter]->m_lSectionOffset) && (tOffset <= m_LineSections[iCounter]->m_lSectionOffset+m_LineSections[iCounter]->m_lSectionLength))
{
// the character is a part of a section.
*iFadeStep = m_LineSections[iCounter]->m_iFadeStep;
bReturn = true;
// return true that the character is not normal
return true;
}
}
}
// character is not part of a section.
*iFadeStep = 0;
}
else
{
// no sections
*iFadeStep = 0;
}
}
// return true if the character is not normal
return bReturn;
}
bool CABLogBuffer::AddData(BYTE * pData, long lData)
{
// this will add the data to the buffer
// and create a new section for the added data
long lNewStart;
long lNewEnd;
if (lData < 1)
// no data to be added
return false;
if (m_lDataLength == 0)
lNewStart = 0;
else
lNewStart = m_lDataLength;
lNewEnd = lData + m_lDataLength;
// make sure that the data is big enough
CheckDataBufferSize(lNewEnd);
// copy the data in and the process it.
memcpy(&m_pDataBuffer[lNewStart], pData, lData);
ProcessBuffer(m_pDataBuffer, lNewStart, lNewEnd);
AddSection(lNewStart, lNewEnd - lNewStart);
m_lDataLength += lData;
return true;
}
bool CABLogBuffer::FadeSections()
{
// this will decrease the fade count of all sections
int iCounter;
if (m_lSectionCount == 0)
return false;
for (iCounter = 0; iCounter < _MAX_SECTIONS; iCounter++)
{
if (m_LineSections[iCounter] != NULL)
{
m_LineSections[iCounter]->m_iFadeStep--;
if (m_LineSections[iCounter]->m_iFadeStep == 0)
{
// this section has expired, remove the section.
free(m_LineSections[iCounter]);
m_LineSections[iCounter] = NULL;
m_lSectionCount--;
}
}
}
return true;
}
bool CABLogBuffer::AddSection(long lStart, long lLength)
{
// this addes a new section
int iCounter;
for (iCounter = 0; iCounter < _MAX_SECTIONS; iCounter++)
{
if (m_LineSections[iCounter] == NULL)
{
m_LineSections[iCounter] = (S_ABLogSect *)malloc(sizeof(S_ABLogSect));
S_ABLogSect * pSect;
pSect = m_LineSections[iCounter];
pSect->m_iFadeStep = FADE_STEPS -1;
pSect->m_lSectionLength = lLength -1;
pSect->m_lSectionOffset = lStart;
m_lSectionCount++;
return true;
}
}
return false;
}
bool CABLogBuffer::CheckByte(BYTE pByte)
{
//check if pByte is any of the delimiter posibilities
if (pByte == '\r') return true;
if (pByte == '\n') return true;
if (pByte == '\f') return true;
return false;
}
bool CABLogBuffer::FindDelim(BYTE * pData, long lLength)
{
// try to work out the delimiter;
// this is not a fool proof process.
// but it will work for most of the cases.
long lCount;
bool bReturn;
bReturn = false;
lCount = 0;
while (lCount < lLength)
{
// go through all the bytes in pData
if (CheckByte(pData[lCount]))
{
// the current byte is a member of one of the delimiters
m_LineDelim = &pData[lCount];
m_iLineDelimLength = 1;
lCount ++;
bReturn = true;
while (lCount < lLength)
{
// keep going until the no more delimiters are found.
if (!CheckByte(pData[lCount]))
break;
m_iLineDelimLength++;
lCount ++;
}
break;
}
lCount++;
}
if (bReturn == false)
{
// no delimiter found.
m_iLineDelimLength = 0;
m_LineDelim = &LINE_DELIM;
}
return bReturn;
}
BYTE * CABLogBuffer::DelimSearch(BYTE * pData, long lLength)
{
// is an undefined or single char Delimiter
if ((m_iLineDelimLength == 0) || (m_iLineDelimLength ==1))
return (BYTE *)memchr(pData, *m_LineDelim, lLength);
BYTE * pFind;
long lCount;
lCount = 0;
while (lCount < lLength)
{
pFind = (BYTE *)memchr(pData, *m_LineDelim, lLength);
if (pFind == NULL)
return NULL;
lLength -= (long)(pData - pFind);
pData = pFind;
if (memcmp(pData, m_LineDelim, m_iLineDelimLength)== 0)
return pData;
}
return NULL;
}
bool CABLogBuffer::ProcessBuffer(BYTE * pData, long lStartOffset, long lEndOffset)
{
long lDataLeft;
long lFindSize;
BYTE * pFind;
BYTE * pStart;
bool bFirst;
bFirst = true;
lDataLeft = lEndOffset - lStartOffset;
pStart = &pData[lStartOffset];
while (1)
{
// if there is no delimiter try to wokr out what it is.
if (m_iLineDelimLength == 0)
FindDelim(pStart, lDataLeft);
// search for the delimiter
pFind = DelimSearch(pStart, lDataLeft);
if (pFind == NULL)
// delimiter not found.
// make lFindSize to the length of the dat aleft
lFindSize = (long)(&pData[lEndOffset] - pStart);
else
lFindSize = (long)(pFind - pStart);
if (bFirst)
{
// only do this once.
bFirst = false;
if (m_lLineCount > 0)
{
// if there are any previous lines
// add the text to the previous line.
ABLogLine * pLine;
pLine = GetLine(m_lLineCount -1);
pLine->m_lLineLength += lFindSize;
if (m_lLineBiggest < pLine->m_lLineLength)
m_lLineBiggest = pLine->m_lLineLength;
if (lFindSize == 0)
{
// if the line starts with a Delimiter, add a blank line.
AddLine(pStart + m_iLineDelimLength, 0);
pStart+= lFindSize + m_iLineDelimLength;
lDataLeft-= lFindSize + m_iLineDelimLength;
}
}
else
// just add the line
AddLine(pStart, lFindSize);
}
else
// just add the line
AddLine(pStart, lFindSize);
// increase pointer by the line size and the delimiter.
pStart+= lFindSize + m_iLineDelimLength;
lDataLeft-= lFindSize + m_iLineDelimLength;
// delimiter wan't found last time so end the loop
if (pFind == NULL)
break;
}
return true;
}
CABLogBuffer::S_ABLogLine * CABLogBuffer::AddLine(BYTE * pLineStart, long lLineEnd)
{
// add the line to the lineBuffer
int iLineSize;
S_ABLogLine * pLine;
if (lLineEnd < 0)
return NULL;
iLineSize = sizeof(S_ABLogLine);
CheckLineBufferSize(iLineSize + m_lLineLength);
pLine = &((S_ABLogLine *)m_pLineBuffer)[m_lLineCount];
memset(pLine, 0x00, iLineSize);
pLine->m_pLinePointer = pLineStart;
pLine->m_lLineLength = lLineEnd;
pLine->m_lLineOffset = (long)((long)pLineStart - (long)m_pDataBuffer);
pLine->m_dFlags = 0;
pLine->m_lLineIndex = m_lLineCount;
if (m_lLineBiggest < lLineEnd)
m_lLineBiggest = lLineEnd;
m_lLineCount++;
m_lLineLength+= iLineSize;
//TRACE("AddLine %i\n\tm_pLinePointer 0x%08X\n\tm_lLineLength %i\n", m_lLineCount -1, pLine->m_pLinePointer, pLine->m_lLineLength);
return pLine;
}
bool CABLogBuffer::CheckLineBufferSize(long lMinSize)
{
// this will increase the lineBuffer if required
// buffer big enough
if (lMinSize < m_lLineSize)
return false;
long lNewSize;
BYTE * m_pNewBuffer;
lNewSize = lMinSize + LINEBUFFER_GROWSIZE;
m_pNewBuffer = (BYTE *)GlobalAlloc(0x40, lNewSize);
memcpy(m_pNewBuffer, m_pLineBuffer, m_lLineLength);
if (m_pLineBuffer)
GlobalFree((HGLOBAL)m_pLineBuffer);
m_pLineBuffer = (S_ABLogLine *)m_pNewBuffer;
m_lLineSize = lNewSize;
return true;
}
bool CABLogBuffer::CheckDataBufferSize(long lMinSize)
{
// this will increase the DataBuffer if required
// buffer big enough
if (lMinSize < m_lDataSize)
return false;
long lNewSize;
BYTE * m_pNewBuffer;
lNewSize = lMinSize + DATABUFFER_GROWSIZE;
m_pNewBuffer = (BYTE *)GlobalAlloc(0x40, lNewSize);
memcpy(m_pNewBuffer, m_pDataBuffer, m_lDataLength);
if (m_pDataBuffer)
GlobalFree((HGLOBAL)m_pDataBuffer);
m_pDataBuffer = m_pNewBuffer;
m_lDataSize = lNewSize;
return true;
}