// 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