Listing 2: FileFindDriver.cpp
Implementation of the CFileFindDriver class and associated private classes
// Code written by Graham Pearson, who can be reached at [email protected] // FileFindDriver.cpp: implementation of the CFileFindDriver class and associated private classes // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "FileFindDriver.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Abstract base class class CSearchObject { protected: CSearchObject() { } // Destructor must be virtual virtual ~CSearchObject() { } // Pure virtual functions virtual CString GetPathPrefix() const = 0; virtual int GetNext(CString& sEntry) = 0; // Values returned by GetNext() enum { entryIsFolder = 0, noMoreFolders, entryIsFileSpec, noMoreFileSpecs, entryIsFile, noMoreFiles }; virtual const CFileFind* GetFileFind() const { return NULL; } virtual int GetFilesFound() const { return 0; } friend class CFileFindDriver; }; ////////////////////////////////////////////////////////////////////// class CSearchForFolders : public CSearchObject { const CString m_sPathPrefix; CFileFind m_folderFind; // Status of previous call to FindFile() or FindNextFile() BOOL m_bCanFindNextFolder; // Total number of files (*.*) in this folder int m_iFilesFound; public: CSearchForFolders(const CString& sPathPrefix); virtual ~CSearchForFolders() { m_folderFind.Close(); } virtual CString GetPathPrefix() const { return m_sPathPrefix; } virtual int GetNext(CString& sEntry); virtual const CFileFind* GetFileFind() const { return &m_folderFind; } virtual int GetFilesFound() const { return m_iFilesFound; } }; CSearchForFolders::CSearchForFolders(const CString& sPathPrefix) : m_sPathPrefix(sPathPrefix), m_bCanFindNextFolder(FALSE), m_iFilesFound(0) { ASSERT(!m_sPathPrefix.IsEmpty()); ASSERT(TCHAR('\\') == m_sPathPrefix.GetAt(m_sPathPrefix.GetLength() - 1)); // Prepare the CFileFind object for call to FindNextFile() m_bCanFindNextFolder = m_folderFind.FindFile(CString(m_sPathPrefix + _T("*.*"))); } // GetNext is a virtual function int CSearchForFolders::GetNext(CString& sEntry) { // Initialize returned values sEntry.Empty(); int iRetVal = noMoreFolders; while (m_bCanFindNextFolder) { // Continue search process m_bCanFindNextFolder = m_folderFind.FindNextFile(); // Skip . and .. files; otherwise, we'd recurse infinitely! if (m_folderFind.IsDots()) continue; // Watch for directory (folder) entries if (m_folderFind.IsDirectory()) { // Return entry to caller sEntry = m_folderFind.GetFilePath(); iRetVal = entryIsFolder; break; } // This entry is a file (not necessarily a matching file) // Keep track of the total number of files found in this folder ++m_iFilesFound; } return iRetVal; } ////////////////////////////////////////////////////////////////////// class CSearchForFileSpec : public CSearchObject { const CString m_sPathPrefix; // A copy of the CFileFindDriver constructor argument const CStringArray& m_sFileSpecArray; const int m_iArraySize; int m_iArrayPos; public: CSearchForFileSpec(const CString& sPathPrefix, const CStringArray& sFileSpecArray); virtual ~CSearchForFileSpec() { } virtual CString GetPathPrefix() const { return m_sPathPrefix; } virtual int GetNext(CString& sEntry); }; CSearchForFileSpec::CSearchForFileSpec(const CString& sPathPrefix, const CStringArray& sFileSpecArray) : m_sPathPrefix(sPathPrefix), m_sFileSpecArray(sFileSpecArray), m_iArraySize(sFileSpecArray.GetSize()) { ASSERT(!m_sPathPrefix.IsEmpty()); ASSERT(TCHAR('\\') == m_sPathPrefix.GetAt(m_sPathPrefix.GetLength() - 1)); ASSERT(0 < m_iArraySize); m_iArrayPos = -1; } // GetNext is a virtual function int CSearchForFileSpec::GetNext(CString& sEntry) { // Initialize returned values sEntry.Empty(); int iRetVal = noMoreFileSpecs; ++m_iArrayPos; if (m_iArraySize > m_iArrayPos) { sEntry = m_sPathPrefix + m_sFileSpecArray.GetAt(m_iArrayPos); iRetVal = entryIsFileSpec; } return iRetVal; } ////////////////////////////////////////////////////////////////////// class CSearchForFiles : public CSearchObject { const CString m_sSearchSpec; CFileFind m_fileFind; // Status of previous call to FindFile() or FindNextFile() BOOL m_bCanFindNextFile; // Number of files in this folder that match m_sSearchSpec int m_iFilesFound; public: CSearchForFiles(const CString& sSearchSpec); virtual ~CSearchForFiles() { m_fileFind.Close(); } virtual CString GetPathPrefix() const; virtual int GetNext(CString& sEntry); virtual const CFileFind* GetFileFind() const { return &m_fileFind; } virtual int GetFilesFound() const { return m_iFilesFound; } }; CSearchForFiles::CSearchForFiles(const CString& sSearchSpec) : m_sSearchSpec(sSearchSpec), m_bCanFindNextFile(FALSE), m_iFilesFound(0) { ASSERT(!m_sSearchSpec.IsEmpty()); // Prepare the CFileFind object for call to FindNextFile() m_bCanFindNextFile = m_fileFind.FindFile(m_sSearchSpec); } // GetPathPrefix is a virtual function CString CSearchForFiles::GetPathPrefix() const { const int iBackSlashPos = m_sSearchSpec.ReverseFind(TCHAR('\\')); ASSERT(-1 != iBackSlashPos); // + 1: include trailing backSlash in returned string return m_sSearchSpec.Left(iBackSlashPos + 1); } // GetNext is a virtual function int CSearchForFiles::GetNext(CString& sEntry) { // Initialize returned values sEntry.Empty(); int iRetVal = noMoreFiles; while (m_bCanFindNextFile) { // Continue search process m_bCanFindNextFile = m_fileFind.FindNextFile(); // Skip . and .. files; otherwise, we'd recurse infinitely! if (m_fileFind.IsDots()) continue; // Skip any directory (folder) entries matching this specification if (m_fileFind.IsDirectory()) continue; // This entry is a matching file // Return entry to caller sEntry = m_fileFind.GetFilePath(); iRetVal = entryIsFile; // Keep track of the number of matching files found in this folder ++m_iFilesFound; break; } return iRetVal; } ////////////////////////////////////////////////////////////////////// // Use the CFileFindDriver class as follows: // _T("*.*") = process all files // TRUE = recurse into subdirectories // for (CFileFindDriver driver(sSearchDir, _T("*.*"), TRUE); (NULL != driver.GetFileFind()); driver++) // { // // Call any of the CFileFind const functions, e.g. // CString sFilePath(driver.GetFileFind()->GetFilePath()); // CString sFileName(driver.GetFileFind()->GetFileName()); // CString sFileTitle(driver.GetFileFind()->GetFileTitle()); // etc. // } // // Alternatively, search for all .cpp and .h files: // CStringArray sFileSpecArray; // sFileSpecArray.SetSize(2); // sFileSpecArray.SetAt(0, _T("*.cpp")); // sFileSpecArray.SetAt(1, _T("*.h")); // TRUE = recurse into subdirectories // for (CFileFindDriver driver(sSearchDir, sFileSpecArray, TRUE); (NULL != driver.GetFileFind()); driver++) // { // // Call any of the CFileFind const functions, e.g. // CString sFilePath(driver.GetFileFind()->GetFilePath()); // CString sFileName(driver.GetFileFind()->GetFileName()); // CString sFileTitle(driver.GetFileFind()->GetFileTitle()); // etc. // } CFileFindDriver::CFileFindDriver(const CString& sSearchDir, const CString& sSearchSpec, BOOL bRecurse) : m_sSearchDir(sSearchDir), m_bRecurse(bRecurse), m_pFileFind(NULL), m_iFilesFound(0), m_iSpecArrayPos(0) { ASSERT(!m_sSearchDir.IsEmpty()); // Build m_sFileSpecArray from sSearchSpec ASSERT(!sSearchSpec.IsEmpty()); m_sFileSpecArray.SetSize(1); m_sFileSpecArray.SetAt(0, sSearchSpec); // Initialize search position First(); } CFileFindDriver::CFileFindDriver(const CString& sSearchDir, const CStringArray& sFileSpecArray, BOOL bRecurse) : m_sSearchDir(sSearchDir), m_bRecurse(bRecurse), m_pFileFind(NULL), m_iFilesFound(0), m_iSpecArrayPos(0) { ASSERT(!m_sSearchDir.IsEmpty()); // Build m_sFileSpecArray from sFileSpecArray const int iSpecArraySize = sFileSpecArray.GetSize(); ASSERT(0 < iSpecArraySize); m_sFileSpecArray.SetSize(iSpecArraySize); for (int iArrayPos = 0; iSpecArraySize > iArrayPos; iArrayPos++) { m_sFileSpecArray.SetAt(iArrayPos, sFileSpecArray.GetAt(iArrayPos)); } // Initialize search position First(); } CFileFindDriver::~CFileFindDriver() { // Make sure all objects in m_searchList are destroyed EmptySearchList(); } // Private "helper" function void CFileFindDriver::EmptySearchList() { // Destroy all CSearchObjects attached to m_searchList, from tail to head POSITION pos = m_searchList.GetTailPosition(); while (NULL != pos) delete m_searchList.GetPrev(pos); // Empty the list m_searchList.RemoveAll(); } // Start / restart search process BOOL CFileFindDriver::First() { // Reinitialize member data EmptySearchList(); m_sSearchDirList.RemoveAll(); m_foundList.RemoveAll(); m_iFilesFound = m_iSpecArrayPos = 0; // If necessary, append a trailing backSlash to m_sSearchDir CString sPathPrefix(m_sSearchDir); if (TCHAR('\\') != sPathPrefix.GetAt(sPathPrefix.GetLength() - 1)) sPathPrefix += TCHAR('\\'); // Keep track of all the directories found in this search process m_sSearchDirList.AddTail(sPathPrefix.Left(sPathPrefix.GetLength() - 1)); if (m_bRecurse) // Append a new CSearchForFolders object ready for searching m_searchList.AddTail(new CSearchForFolders(sPathPrefix)); else // Append a new CSearchForFileSpec object ready for searching m_searchList.AddTail(new CSearchForFileSpec(sPathPrefix, m_sFileSpecArray)); // Navigate through files return Next(); } // Navigate through files BOOL CFileFindDriver::Next() { // Reset member variable m_pFileFind = NULL; BOOL bSearching = TRUE; while (bSearching) { // If our m_searchList now is empty, we're all done if (m_searchList.IsEmpty()) break; // Access the tail of our m_searchList CSearchObject* pEntry = m_searchList.GetTail(); ASSERT(NULL != pEntry); // Get the next entry associated with this search object CString sEntry; // Call virtual function const int iResponse = pEntry->GetNext(sEntry); switch (iResponse) { case CSearchObject::entryIsFolder: // Keep track of all the directories found in this search process m_sSearchDirList.AddTail(sEntry); // Append a new CSearchForFolders object ready for searching m_searchList.AddTail(new CSearchForFolders(CString(sEntry + TCHAR('\\')))); break; case CSearchObject::noMoreFolders: { // Retrieve search results const CString sPathPrefix(pEntry->GetPathPrefix()); const int iFilesFound = pEntry->GetFilesFound(); // Remove the tail element from m_searchList delete pEntry; m_searchList.RemoveTail(); // Did this object discover any files? if (0 < iFilesFound) { // Append a new CSearchForFileSpec object ready for searching m_searchList.AddTail(new CSearchForFileSpec(sPathPrefix, m_sFileSpecArray)); // Prepare member variables that make sure a specific file is returned // no more than once by the CSearchObject::entryIsFile case m_foundList.RemoveAll(); m_iSpecArrayPos = 0; } } break; case CSearchObject::entryIsFileSpec: // Append a new CSearchForFiles object ready for searching m_searchList.AddTail(new CSearchForFiles(sEntry)); // Update count for use by CSearchObject::entryIsFile case ++m_iSpecArrayPos; break; case CSearchObject::noMoreFileSpecs: // Remove the tail element from m_searchList and keep searching // i.e. search the new tail element by repeating this loop delete pEntry; m_searchList.RemoveTail(); break; case CSearchObject::entryIsFile: // Do we need to check this file against the list // of files retrieved from this folder so far? if (1 < m_iSpecArrayPos) { // Have we already retrieved this file? if (NULL != m_foundList.Find(sEntry)) continue; } // Keep a list of files retrieved so far from this folder m_foundList.AddTail(sEntry); // Keep track of the number of matching files found during this search ++m_iFilesFound; // Call virtual function m_pFileFind = pEntry->GetFileFind(); ASSERT(NULL != m_pFileFind); bSearching = FALSE; break; case CSearchObject::noMoreFiles: // Remove the tail element from m_searchList and keep searching // i.e. search the new tail element by repeating this loop delete pEntry; m_searchList.RemoveTail(); break; default: // Programmer's reality check VERIFY(FALSE); } } return (NULL == m_pFileFind) ? FALSE : TRUE; } // This function returns a list of all directories searched since // CFileFindDriver object construction or since the last call to First() void CFileFindDriver::GetSearchDirList(CStringList& sSearchDirList) const { // CStringList has neither a copy constructor nor an assignment operator // so we must copy each element of m_sSearchDirList explicitly sSearchDirList.RemoveAll(); POSITION pos = m_sSearchDirList.GetHeadPosition(); while (NULL != pos) sSearchDirList.AddTail(m_sSearchDirList.GetNext(pos)); }