Click here to Skip to main content
15,891,136 members
Articles / Desktop Programming / MFC

A real time log file viewer

Rate me:
Please Sign up or sign in to vote.
4.81/5 (19 votes)
17 May 20042 min read 172.6K   4.5K   58  
A real time log file viewer.
/* 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;
}



By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Australia Australia
Started programming many many years ago, now a full time C++ developer writing medical software. I have also been known to dabble in electronics. Currently looking to go back to university to study electronic engineering.

Comments and Discussions