440 lines
9.1 KiB
C++
440 lines
9.1 KiB
C++
// SearchThread.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "grep.h"
|
|
#include "SearchThread.h"
|
|
#include "SearchOptions.h"
|
|
#include "MainFrm.h"
|
|
#include "GrepView.h"
|
|
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CSearchThread
|
|
|
|
IMPLEMENT_DYNCREATE(CSearchThread, CWinThread)
|
|
|
|
|
|
long GetLineNumber(TCHAR* lpszFileBuffer, int nIndex)
|
|
{
|
|
long nLine = 1;
|
|
while (nIndex--)
|
|
{
|
|
if (*lpszFileBuffer == _T('\n')) nLine++;
|
|
lpszFileBuffer++;
|
|
}
|
|
|
|
return nLine;
|
|
}
|
|
|
|
void CSearchThread::AddRecord(CXTPReportControl& wndControl, TCHAR* lpszFileBuffer, TCHAR* lpszEndBuffer, long nIndex, long nLength,
|
|
CString strMatch, CString strReplace, CString strPath, CFileFind& finder)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
TCHAR* lpszMatch = lpszFileBuffer + nIndex;
|
|
TCHAR* lpszMatchBeginLine = lpszMatch;
|
|
TCHAR* lpszMatchEndLine = lpszMatchBeginLine + nLength;
|
|
|
|
int nMaxPreview = 80;
|
|
|
|
while (nMaxPreview-- && lpszMatchBeginLine != lpszFileBuffer && *(lpszMatchBeginLine - 1) != '\n')
|
|
lpszMatchBeginLine--;
|
|
|
|
if (*lpszMatchEndLine != '\n')
|
|
{
|
|
nMaxPreview = 80;
|
|
|
|
while (nMaxPreview-- && lpszMatchEndLine != lpszEndBuffer && *(lpszMatchEndLine + 1) != '\n' )
|
|
lpszMatchEndLine++;
|
|
}
|
|
TCHAR tchEndLine = *lpszMatchEndLine;
|
|
*lpszMatchEndLine = '\0';
|
|
|
|
|
|
CString strBeginLine;
|
|
long nBeginLineLength = long(lpszMatch - lpszMatchBeginLine);
|
|
STRNCPY_S(strBeginLine.GetBufferSetLength(nBeginLineLength), nBeginLineLength + 1, lpszMatchBeginLine, nBeginLineLength);
|
|
|
|
|
|
CString strEndLine;
|
|
long nEndLineLength = long(lpszMatchEndLine - lpszMatch - nLength);
|
|
STRNCPY_S(strEndLine.GetBufferSetLength(nEndLineLength), nEndLineLength + 1, lpszMatch + nLength, nEndLineLength);
|
|
|
|
wndControl.AddRecord(new CGrepRecord
|
|
(finder.GetFilePath(), strPath, finder.GetFileName(), strMatch,
|
|
GetLineNumber(lpszFileBuffer, nIndex),
|
|
strBeginLine + strMatch + strEndLine,
|
|
strBeginLine + strReplace + strEndLine, nIndex, nLength, strReplace));
|
|
|
|
*lpszMatchEndLine = tchEndLine;
|
|
|
|
m_searchResult.nMatchingLines++;
|
|
}
|
|
|
|
void CSearchThread::SetMessageText()
|
|
{
|
|
CString strMessage;
|
|
strMessage.Format(_T("Matching lines: %i Matching files: %i Total files searched: %i"),
|
|
m_searchResult.nMatchingLines, m_searchResult.nMatchingFiles, m_searchResult.nTotalFiles);
|
|
|
|
m_pMainFrame->SetMessageText(strMessage);
|
|
m_pMainFrame->DelayPopulate();
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
#define CT2BSTR(x) (BSTR)(LPCWSTR)x
|
|
#else
|
|
#define CT2BSTR(x) (BSTR)A2CW(x)
|
|
#endif
|
|
|
|
int FindNext(const CString& str, const CString& strFind, int nIndex, CSearchOptions* pOptions)
|
|
{
|
|
nIndex = FIND_S(str, strFind, nIndex);
|
|
|
|
if (!pOptions->m_bMatchWholeWords)
|
|
return nIndex;
|
|
|
|
static LPCTSTR szSeps = _T("() \t<>{}:;,.=%\"'!@#$^&*-\\|[]/?\r\n");
|
|
|
|
|
|
while (nIndex != -1)
|
|
{
|
|
|
|
BOOL bSepAfter = FALSE;
|
|
BOOL bSepBefore = FALSE;
|
|
|
|
if (nIndex + strFind.GetLength() >= str.GetLength())
|
|
bSepAfter = TRUE;
|
|
else
|
|
bSepAfter = (_tcschr(szSeps, str[nIndex + strFind.GetLength()]) != 0);
|
|
|
|
if (nIndex == 0)
|
|
bSepBefore = TRUE;
|
|
else
|
|
bSepBefore = (_tcschr(szSeps, str[nIndex - 1]) != 0);
|
|
|
|
if (bSepAfter && bSepBefore)
|
|
return nIndex;
|
|
|
|
nIndex = FIND_S(str, strFind, nIndex + 1);
|
|
|
|
}
|
|
return nIndex;
|
|
}
|
|
|
|
BOOL CSearchThread::ProcessFile(CString strPath, CFileFind& finder)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
if (!m_pReportControl)
|
|
return FALSE;
|
|
|
|
CSearchOptions* pOptions = GetSearchOptions();
|
|
ASSERT(pOptions);
|
|
|
|
CFile file;
|
|
if (!file.Open(finder.GetFilePath(), CFile::modeRead))
|
|
return TRUE;
|
|
|
|
DWORD dwCount = (DWORD)file.GetLength();
|
|
|
|
char* pchData = new char[dwCount + 1];
|
|
file.Read(pchData, dwCount);
|
|
pchData[dwCount] = 0;
|
|
|
|
#ifdef _UNICODE
|
|
TCHAR* lpszFileBuffer = new TCHAR[dwCount + 1];
|
|
_mbstowcsz(lpszFileBuffer, pchData, dwCount+1);
|
|
#else
|
|
char* lpszFileBuffer = pchData;
|
|
#endif
|
|
|
|
|
|
file.Close();
|
|
TCHAR* lpszEndBuffer = lpszFileBuffer + dwCount;
|
|
|
|
|
|
BOOL bContinue = TRUE;
|
|
|
|
|
|
if (!pOptions->pRegExp)
|
|
{
|
|
CString str(lpszFileBuffer);
|
|
CString strFind = pOptions->m_strFind;
|
|
|
|
if (!pOptions->m_bMatchCase)
|
|
{
|
|
str.MakeLower();
|
|
strFind.MakeLower();
|
|
}
|
|
|
|
int nLength = strFind.GetLength();
|
|
int nIndex = FindNext(str, strFind, 0, pOptions);
|
|
|
|
if (nIndex != -1)
|
|
{
|
|
m_searchResult.nMatchingFiles++;
|
|
}
|
|
|
|
while (nIndex != -1)
|
|
{
|
|
CString strMatch;
|
|
STRNCPY_S(strMatch.GetBufferSetLength(nLength), nLength + 1, lpszFileBuffer + nIndex, nLength);
|
|
|
|
if (m_bCancel)
|
|
{
|
|
bContinue = FALSE;
|
|
break;
|
|
}
|
|
|
|
CString strReplace = pOptions->m_strReplace;
|
|
AddRecord(*m_pReportControl, lpszFileBuffer, lpszEndBuffer, nIndex, nLength,
|
|
strMatch, strReplace, strPath, finder);
|
|
|
|
nIndex = FindNext(str, strFind, nIndex + 1, pOptions);
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
try
|
|
{
|
|
#ifndef _UNICODE
|
|
BSTR bstrBuffer = new WCHAR[dwCount + 1];
|
|
_mbstowcsz(bstrBuffer, lpszFileBuffer, dwCount+1);
|
|
#else
|
|
BSTR bstrBuffer = (BSTR)lpszFileBuffer;
|
|
#endif
|
|
IMatchCollectionPtr MatchesPtr = pOptions->pRegExp->Execute(bstrBuffer);
|
|
|
|
int nCount = MatchesPtr.GetInterfacePtr()? MatchesPtr->Count: 0;
|
|
|
|
if (nCount > 0)
|
|
{
|
|
m_searchResult.nMatchingFiles++;
|
|
}
|
|
|
|
for (int i = 0; i < nCount; i++)
|
|
{
|
|
IMatchPtr MatchPtr = MatchesPtr->Item[i];
|
|
|
|
int nIndex = MatchPtr->FirstIndex;
|
|
int nLength =MatchPtr->Length;
|
|
|
|
CString strMatch;
|
|
STRNCPY_S(strMatch.GetBufferSetLength(nLength), nLength + 1, lpszFileBuffer + nIndex, nLength);
|
|
|
|
_bstr_t bstrResult =pOptions->pRegExp->Replace(CT2BSTR(strMatch), CT2BSTR(pOptions->m_strReplace));
|
|
CString strReplace = (LPCWSTR)bstrResult;
|
|
|
|
if (m_bCancel)
|
|
{
|
|
bContinue = FALSE;
|
|
break;
|
|
}
|
|
|
|
AddRecord(*m_pReportControl, lpszFileBuffer, lpszEndBuffer, nIndex, nLength,
|
|
strMatch, strReplace, strPath, finder);
|
|
|
|
}
|
|
#ifndef _UNICODE
|
|
delete[] bstrBuffer;
|
|
#endif
|
|
|
|
}
|
|
catch (_com_error& e)
|
|
{
|
|
CString strDescription(BSTR(e.Description()));
|
|
if (strDescription.IsEmpty())
|
|
strDescription = _T("Error in regular expression.");
|
|
strDescription += " Continue?";
|
|
|
|
bContinue = AfxMessageBox(strDescription, MB_YESNO) == IDNO? FALSE: TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
delete[] lpszFileBuffer;
|
|
|
|
#ifdef _UNICODE
|
|
delete[] pchData;
|
|
#endif
|
|
|
|
if (bContinue)
|
|
{
|
|
SetMessageText();
|
|
}
|
|
|
|
return bContinue;
|
|
|
|
}
|
|
|
|
BOOL MatchFileType(CString strFileName, CString strMask)
|
|
{
|
|
if (strMask == _T("*.*"))
|
|
return TRUE;
|
|
|
|
strMask.MakeLower();
|
|
strFileName.MakeLower();
|
|
|
|
strFileName += _T(";");
|
|
strMask += _T(";");
|
|
|
|
CString str = strMask;
|
|
while (str != _T(";") && str != _T(""))
|
|
{
|
|
int nIndex = str.Find(_T(';'));
|
|
if (nIndex != -1)
|
|
{
|
|
strMask = str.Mid(nIndex + 1);
|
|
str = str.Left(nIndex + 1);
|
|
}
|
|
else
|
|
{
|
|
strMask.Empty();
|
|
}
|
|
|
|
REPLACE_S(str, _T("*;"), _T(""));
|
|
REPLACE_S(str, _T(";*"), _T(""));
|
|
REPLACE_S(str, _T("*"), _T(""));
|
|
|
|
if (strFileName.Find(str) >= 0)
|
|
return TRUE;
|
|
|
|
str = strMask;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL CSearchThread::ProcessPath(CString strPath)
|
|
{
|
|
CFileFind finder;
|
|
|
|
CString strWildcard = strPath + _T("\\*.*") ;
|
|
|
|
// start working for files
|
|
BOOL bWorking = finder.FindFile(strWildcard);
|
|
|
|
while (bWorking)
|
|
{
|
|
bWorking = finder.FindNextFile();
|
|
|
|
// skip . and .. files; otherwise, we'd
|
|
// recur infinitely!
|
|
|
|
if (finder.IsDots())
|
|
continue;
|
|
|
|
// if it's a directory, recursively search it
|
|
|
|
if (finder.IsDirectory())
|
|
{
|
|
if (GetSearchOptions()->m_bIncludeSubFolders)
|
|
{
|
|
if (GetSearchOptions()->m_bFindInHiddenFiles || !finder.IsHidden())
|
|
{
|
|
if (!ProcessPath(finder.GetFilePath()))
|
|
return FALSE;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (MatchFileType(finder.GetFileName(), GetSearchOptions()->m_strFileTypes))
|
|
{
|
|
if (GetSearchOptions()->m_bFindInHiddenFiles || !finder.IsHidden())
|
|
{
|
|
m_searchResult.nTotalFiles++;
|
|
|
|
if (!ProcessFile(strPath, finder))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (m_bCancel)
|
|
return FALSE;
|
|
}
|
|
|
|
finder.Close();
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
CSearchThread::CSearchThread()
|
|
{
|
|
m_pMainFrame = NULL;
|
|
m_pReportControl = NULL;
|
|
m_bAutoDelete = TRUE;
|
|
m_bCancel = FALSE;
|
|
|
|
ZeroMemory(&m_searchResult, sizeof(SEARCH_RESULT));
|
|
}
|
|
|
|
CSearchThread::~CSearchThread()
|
|
{
|
|
}
|
|
|
|
BOOL CSearchThread::InitInstance()
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
AfxOleInit();
|
|
|
|
IRegExpPtr regExp;
|
|
|
|
CSearchOptions* pOptions = GetSearchOptions();
|
|
ASSERT(pOptions);
|
|
|
|
|
|
if (pOptions->m_bRegularExpressions)
|
|
{
|
|
HRESULT hr = regExp.CreateInstance(__uuidof(RegExp));
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
AfxMessageBox(_T("RegExp not found"));
|
|
}
|
|
else
|
|
{
|
|
pOptions->pRegExp = regExp;
|
|
regExp->Global = VARIANT_TRUE;
|
|
regExp->IgnoreCase = pOptions->m_bMatchCase? VARIANT_FALSE: VARIANT_TRUE;
|
|
regExp->Pattern = _bstr_t(CT2BSTR(pOptions->m_strFind));
|
|
}
|
|
}
|
|
|
|
ProcessPath(pOptions->m_strPath);
|
|
|
|
pOptions->pRegExp = 0;
|
|
|
|
// TODO: perform and per-thread initialization here
|
|
return FALSE;
|
|
}
|
|
|
|
int CSearchThread::ExitInstance()
|
|
{
|
|
m_pMainFrame->PostMessage(WM_SEARCHFINISHED);
|
|
|
|
// TODO: perform any per-thread cleanup here
|
|
return CWinThread::ExitInstance();
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CSearchThread, CWinThread)
|
|
//{{AFX_MSG_MAP(CSearchThread)
|
|
// NOTE - the ClassWizard will add and remove mapping macros here.
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CSearchThread message handlers
|