Click here to Skip to main content
15,885,985 members
Articles / General Programming / Algorithms

Iterative Implementation of Recursively Enumerating Files and Sub Folders

Rate me:
Please Sign up or sign in to vote.
4.64/5 (19 votes)
30 Dec 2010CPOL3 min read 59.4K   3.6K   51  
Yet another implementation to enumerate files
/* 
 * Kenny Liu
 * http://www.codeproject.com/Members/yonken
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose,  provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

#include "stdafx.h"

#include "FileEnumerator.h"

#include <tchar.h>

#if !defined(_AFX) && !defined(_ATL)
void _cfe_trace( LPCTSTR fmt, ... )
{
	TCHAR buf[2000];
	va_list args;
	
	va_start( args, fmt );
#ifdef MSVC_NEW_VER
	_vsntprintf_s( buf, sizeof(buf), _TRUNCATE, fmt, args );
#else
	_vsntprintf( buf, sizeof(buf), fmt, args );
#endif // MSVC_NEW_VER
	va_end( args );
	
	::OutputDebugString(buf);
}
#endif

/*----------------------------------------------------------------------------*/
/* CFileFinder
/*----------------------------------------------------------------------------*/

#define USE_STL_STRING_AS_MEMBER	// use heap instead of stack

#ifdef USE_STL_STRING_AS_MEMBER
	#define STRBUFFER(_str)			_str.c_str()
#else
	#define STRBUFFER(_str)			_str
#endif // USE_STL_STRING_AS_MEMBER

class CFileFinder
{
public:
	CFileFinder(LPCTSTR lpcszInitDir, FindFileData& fileInfo)
		: m_fileInfo(fileInfo)
	{
		Init(lpcszInitDir);
#ifdef _DEBUG
		++s_nInstances;
#endif // _DEBUG
	}

	virtual ~CFileFinder()
	{
#ifdef _DEBUG
		--s_nInstances;
#endif // _DEBUG
	}

public:
	inline bool FindFirst()
	{
		return EnumCurDirFirst();
	}

	inline bool FindCurDirNext()
	{
		bool bRet = ::FindNextFile(m_hFind, &m_fileInfo) != FALSE;
		if ( bRet )
		{
#ifdef USE_STL_STRING_AS_MEMBER
			m_szPathBuffer.resize(m_nFolderLen);
			//m_szPathBuffer[m_nFolderLen] = _T('\0');
			m_szPathBuffer += m_fileInfo.cFileName;
#else
	#ifdef MSVC_NEW_VER
			_tcsncpy_s(m_szPathBuffer+m_nFolderLen, ENUMERATE_PATH_BUFFER_SIZE-m_nFolderLen, m_fileInfo.cFileName, _TRUNCATE);
	#else
			_tcsncpy(m_szPathBuffer+m_nFolderLen, m_fileInfo.cFileName, ENUMERATE_PATH_BUFFER_SIZE-m_nFolderLen);
	#endif // MSVC_NEW_VER
#endif // USE_STL_STRING_AS_MEMBER
		}
		else
		{
			::FindClose(m_hFind);
			m_hFind = INVALID_HANDLE_VALUE;
		}
		return bRet;
	}

	virtual bool Finish() const					{ return INVALID_HANDLE_VALUE == m_hFind; }

	inline LPCTSTR GetPath() const
	{
		return STRBUFFER(m_szPathBuffer) + EXTEND_FILE_PATH_PREFIX_LEN;
	}

	inline const FindFileData& GetFileInfo() const	{ return m_fileInfo; }

	inline bool IsDirectory() const				{ return !!(m_fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); }
	inline bool IsDot() const					{ return (m_fileInfo.cFileName[0] == '.') 
												&& ((m_fileInfo.cFileName[1] == '.') || (m_fileInfo.cFileName[1] == '\0')); }

	inline bool	IsReparsePoint() const			{ return !!(m_fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); }
protected:
	virtual bool EnumCurDirFirst()
	{
#ifdef USE_STL_STRING_AS_MEMBER
		m_szPathBuffer.resize(m_nFolderLen+2);
#endif // USE_STL_STRING_AS_MEMBER

		m_szPathBuffer[m_nFolderLen++]	= _T('\\');
		m_szPathBuffer[m_nFolderLen]	= _T('*');
#ifndef USE_STL_STRING_AS_MEMBER
		m_szPathBuffer[m_nFolderLen+1]	= _T('\0');
#endif // USE_STL_STRING_AS_MEMBER

		HANDLE hFind = ::FindFirstFile(STRBUFFER(m_szPathBuffer), &m_fileInfo);
		bool bRet = hFind != INVALID_HANDLE_VALUE;
		if ( bRet )
		{
			m_hFind			= hFind;
#ifdef USE_STL_STRING_AS_MEMBER
			m_szPathBuffer.resize(m_nFolderLen);
			//m_szPathBuffer[m_nFolderLen] = _T('\0');
			m_szPathBuffer += m_fileInfo.cFileName;
#else
	#ifdef MSVC_NEW_VER
			_tcsncpy_s(m_szPathBuffer+m_nFolderLen, ENUMERATE_PATH_BUFFER_SIZE-m_nFolderLen, m_fileInfo.cFileName, _TRUNCATE);
	#else
			_tcsncpy(m_szPathBuffer+m_nFolderLen, m_fileInfo.cFileName, ENUMERATE_PATH_BUFFER_SIZE-m_nFolderLen);
	#endif // MSVC_NEW_VER
#endif // USE_STL_STRING_AS_MEMBER
		}
		return bRet;
	}

#ifdef USE_STL_STRING_AS_MEMBER
	void Init(LPCTSTR lpcszInitDir)
	{
		m_nFolderLen = _tcslen(lpcszInitDir);
		
	#ifdef EXTEND_FILE_PATH_LIMIT
		// MSDN stated:
		// Note  Prepending the string "\\?\" does not allow access to the root directory.
		m_szPathBuffer = EXTEND_FILE_PATH_PREFIX ;
		m_szPathBuffer += lpcszInitDir;
		m_nFolderLen += EXTEND_FILE_PATH_PREFIX_LEN;	// for EXTEND_FILE_PATH_PREFIX
	#else
		m_szPathBuffer = lpcszInitDir;
	#endif // EXTEND_FILE_PATH_LIMIT
		
		if ( m_szPathBuffer[m_nFolderLen-1] == _T('\\') )
		{
			m_szPathBuffer.erase(m_nFolderLen-1);
			--m_nFolderLen;
		}
	}
#else
	void Init(LPCTSTR lpcszInitDir)
	{
		m_nFolderLen = _tcslen(lpcszInitDir);
	#ifdef EXTEND_FILE_PATH_LIMIT
		// MSDN stated:
		// Note  Prepending the string "\\?\" does not allow access to the root directory.
		#ifdef MSVC_NEW_VER
		_tcsncpy_s(m_szPathBuffer, ENUMERATE_PATH_BUFFER_SIZE, EXTEND_FILE_PATH_PREFIX, EXTEND_FILE_PATH_PREFIX_LEN);
		_tcsncat_s(m_szPathBuffer, ENUMERATE_PATH_BUFFER_SIZE, lpcszInitDir, _TRUNCATE);
		#else
		_tcsncpy(m_szPathBuffer, EXTEND_FILE_PATH_PREFIX, ENUMERATE_PATH_BUFFER_SIZE);
		_tcsncat(m_szPathBuffer, lpcszInitDir, ENUMERATE_PATH_BUFFER_SIZE);
		#endif // MSVC_NEW_VER
		m_nFolderLen += EXTEND_FILE_PATH_PREFIX_LEN;	// for EXTEND_FILE_PATH_PREFIX
	#else
		#ifdef MSVC_NEW_VER
		_tcsncpy_s(m_szPathBuffer, ENUMERATE_PATH_BUFFER_SIZE, lpcszInitDir, _TRUNCATE);
		#else
		_tcsncpy(m_szPathBuffer, lpcszInitDir, ENUMERATE_PATH_BUFFER_SIZE);
		#endif // MSVC_NEW_VER
	#endif // EXTEND_FILE_PATH_LIMIT
		
		if ( m_szPathBuffer[m_nFolderLen-1] == _T('\\') )
		{
			m_szPathBuffer[m_nFolderLen-1] = _T('\0');
			--m_nFolderLen;
		}
	}
#endif // USE_STL_STRING_AS_MEMBER

#ifdef _DEBUG
public:
	static inline bool ZeroInstance()
	{
		return 0 == s_nInstances;
	}
#endif // _DEBUG

protected:
	FindFileData&	m_fileInfo;

#ifdef USE_STL_STRING_AS_MEMBER
	tstring			m_szPathBuffer;
#else
	TCHAR			m_szPathBuffer[ENUMERATE_PATH_BUFFER_SIZE];
#endif // USE_STL_STRING_AS_MEMBER

	UINT			m_nFolderLen;
	HANDLE			m_hFind;

#ifdef _DEBUG
	static UINT		s_nInstances;
#endif // _DEBUG
};

#ifdef _DEBUG
UINT CFileFinder::s_nInstances = 0;
#endif // _DEBUG

#if !defined(FILEENUMERATOR_RECURSION) && !defined(FILEENUMERATOR_BFS)
class CDFSFileFinder : public CFileFinder
{
public:
	CDFSFileFinder(LPCTSTR lpcszInitDir, FindFileData& fileInfo)
		: CFileFinder(lpcszInitDir, fileInfo)
		, m_nFolderLevel(0)
	{
	}
	~CDFSFileFinder()
	{
		CFE_ASSERT(m_nFolderLevel <= static_cast<int>(m_arrEnumFolderCtx.size()));
		for (int ii = 0; ii < m_nFolderLevel; ++ii)
		{
			EnumFolderCtx& efc = m_arrEnumFolderCtx[ii];
			CFE_ASSERT(efc.hFind != INVALID_HANDLE_VALUE);
			::FindClose(efc.hFind);
		}
	}
public:
	virtual bool FindSubDirFirst()
	{
		CFE_ASSERT( IsDirectory() && !IsDot() );
		int nCurFolderLen = m_nFolderLen;
		m_nFolderLen += _tcslen(m_fileInfo.cFileName);
		bool bRet = EnumCurDirFirst();
		if ( !bRet )
		{
			m_nFolderLen = nCurFolderLen;
		}
		return bRet;
	}

	virtual bool FindParentDirNext()
	{
		--m_nFolderLevel;
		if ( m_nFolderLevel > 0 )
		{
			CFE_ASSERT( m_nFolderLevel < static_cast<int>(m_arrEnumFolderCtx.size()) );
			EnumFolderCtx& efcParent	= m_arrEnumFolderCtx[m_nFolderLevel-1];
			CFE_ASSERT(m_nFolderLen > efcParent.nFolderPathLen);
			m_nFolderLen				= efcParent.nFolderPathLen;
			m_hFind						= efcParent.hFind;
			return FindCurDirNext();
		}
		return false;
	}

	virtual bool Finish() const
	{
		return CFileFinder::Finish() && 0 == m_nFolderLevel;
	}
protected:
	virtual bool EnumCurDirFirst()
	{
		bool bRet = CFileFinder::EnumCurDirFirst();
		if ( bRet )
		{
			EnumFolderCtx efc(m_hFind, m_nFolderLen);
			if ( m_nFolderLevel >= static_cast<int>(m_arrEnumFolderCtx.size()) )
				m_arrEnumFolderCtx.push_back( efc );
			else
				m_arrEnumFolderCtx[m_nFolderLevel] = efc;
			++m_nFolderLevel;
		}
		return bRet;
	}
protected:
	struct EnumFolderCtx 
	{
		EnumFolderCtx(HANDLE _hFind = NULL, int _nFolderPathLen = -1)
			: hFind(_hFind)
			, nFolderPathLen(_nFolderPathLen)
		{
		}
		HANDLE hFind;
		int nFolderPathLen;
	};
	std::vector<EnumFolderCtx>	m_arrEnumFolderCtx;
	int							m_nFolderLevel;
};
#endif // !defined(FILEENUMERATOR_RECURSION) && !defined(FILEENUMERATOR_BFS)

//////////////////////////////////////////////////////////////////////
// CFileEnumeratorBase
//////////////////////////////////////////////////////////////////////

#define	IsStopEventSignaled()		(::WaitForSingleObject(hStopEvent, 0) == WAIT_OBJECT_0)

CFileEnumeratorBase::CFileEnumeratorBase()
	: m_dwLastError(ERROR_SUCCESS)
{
#ifdef FILEENUMERATOR_DOCOUNTING
	ResetCounter();
#endif // FILEENUMERATOR_DOCOUNTING
}

CFileEnumeratorBase::~CFileEnumeratorBase()
{
}

#ifdef FILEENUMERATOR_DOCOUNTING
	#define IncreaseFindFolderCounter()			(++m_nFindFolderCount)
	#define IncreaseAcceptedFolderCounter()		(++m_nAcceptedFolderCount)
	#define IncreaseFindFileCounter()			(++m_nFindFileCount)
	#define IncreaseAcceptedFindFileCounter()	(++m_nAcceptedFileCount)
#else
	#define IncreaseFindFolderCounter()
	#define IncreaseAcceptedFolderCounter()
	#define IncreaseFindFileCounter()
	#define IncreaseAcceptedFindFileCounter()
#endif // FILEENUMERATOR_DOCOUNTING

#ifdef FILEENUMERATOR_RECURSION
bool CFileEnumeratorBase::EnumerateRecursively( LPCTSTR lpcszInitDir, FindFileData& findFileData, bool bFindSubDir /*= true*/, HANDLE hStopEvent /*= NULL*/ )
{
	CFileFinder fileFinder(lpcszInitDir, findFileData);
	bool bRet = true;
	if ( !fileFinder.FindFirst() )
	{
		m_dwLastError = ::GetLastError();
		return OnError(lpcszInitDir);
	}
	else
	{
		while ( !fileFinder.Finish() && !IsStopEventSignaled() )
		{
			const FindFileData& fileInfo = fileFinder.GetFileInfo();
			if ( !fileFinder.IsReparsePoint() )
			{
				if ( fileFinder.IsDirectory() )
				{
					if ( !fileFinder.IsDot() && bFindSubDir )
					{
						IncreaseFindFolderCounter();
						if ( CheckUseDir(fileFinder.GetPath(), fileInfo) )
						{
							HandleDir(fileFinder.GetPath(), fileInfo);
							IncreaseAcceptedFolderCounter();
							bRet &= EnumerateRecursively(fileFinder.GetPath(), findFileData, bFindSubDir);
						}
					}
				}
				else
				{
					IncreaseFindFileCounter();
					if ( CheckUseFile(fileFinder.GetPath(), fileInfo) )
					{
						HandleFile(fileFinder.GetPath(), fileInfo);
						IncreaseAcceptedFindFileCounter();
					}
				}
			}
			if ( !fileFinder.FindCurDirNext() )
			{
				FinishedDir( fileFinder.GetPath() );
				break;
			}
		}
	}
	return bRet;
}
#else
#ifdef FILEENUMERATOR_BFS

#include <queue>

//#define USE_BOOST_SHARED_PTR

#ifdef USE_BOOST_SHARED_PTR
	#include <boost/shared_ptr.hpp>
	typedef boost::shared_ptr<CFileFinder>	FileFindPtr;
	#define DELETE_FILEFINDER(_pFinder)
#else
	typedef CFileFinder*					FileFindPtr;
	#define DELETE_FILEFINDER(_pFinder)		delete _pFinder
#endif // USE_BOOST_SHARED_PTR

typedef std::queue<FileFindPtr>				FileFindQueue;

bool CFileEnumeratorBase::EnumerateBFS( LPCTSTR lpcszInitDir, FindFileData& findFileData, HANDLE hStopEvent /*= NULL*/ )
{
	// Breadth-first searching, BFS:
	FileFindPtr finder = NULL;
	try
    {
		finder = new CFileFinder(lpcszInitDir, findFileData);
	}
	catch (...)
	{
		CFE_ASSERT(0);
		DELETE_FILEFINDER(finder);
		return false;
	}

	bool bRet = true;
	FileFindQueue finderQueue;
	
    if ( !finder->FindFirst() )
	{
		m_dwLastError = ::GetLastError();
		OnError(finder->GetPath());
		DELETE_FILEFINDER(finder);
		return false;
	}
	else
	{
		while( !finder->Finish() && !IsStopEventSignaled() )
        {
			const FindFileData& fileInfo = finder->GetFileInfo();
			
			// Skips the junction/symbolic link to avoid infinite looping
			if ( !finder->IsReparsePoint() )
			{
				if( finder->IsDirectory() )
				{
					if ( !finder->IsDot() )
					{
						IncreaseFindFolderCounter();
						if ( CheckUseDir(finder->GetPath(), fileInfo) )
						{
							HandleDir(finder->GetPath(), fileInfo);
							IncreaseAcceptedFolderCounter();
							
							FileFindPtr newFinder = NULL;
							try
							{
								newFinder = new CFileFinder(finder->GetPath(), findFileData);
								finderQueue.push(newFinder);
							}
							catch (...)
							{
								CFE_ASSERT(0);
								DELETE_FILEFINDER(newFinder);
							}
						}
					}
				}
				else
				{
					IncreaseFindFileCounter();
					if ( CheckUseFile(finder->GetPath(), fileInfo) )
					{
						HandleFile(finder->GetPath(), fileInfo);
						IncreaseAcceptedFindFileCounter();
					}
				}
			}
            if ( !finder->FindCurDirNext() )
			{
				FinishedDir( finder->GetPath() );
				if ( finderQueue.empty() )
					break;
				else
				{
					while ( !IsStopEventSignaled() )
					{
						FileFindPtr nextFinder = finderQueue.front();
						finderQueue.pop();

						DELETE_FILEFINDER(finder);

						finder = nextFinder;

						if ( !finder->FindFirst() )
						{
							m_dwLastError = ::GetLastError();
							if ( !OnError(finder->GetPath()) )
							{
								bRet = false;
								goto CleanupRet;
							}
						}
						else
							break;
					}
				}
			}
        }
    }
CleanupRet:
	#ifndef USE_BOOST_SHARED_PTR
	while (1)
	{
		DELETE_FILEFINDER(finder);
		if ( finderQueue.empty() )
			break;
		else
		{
			finder = finderQueue.front();
			finderQueue.pop();
		}
	};
	#endif // USE_BOOST_SHARED_PTR
	return bRet;
}

#else

bool CFileEnumeratorBase::EnumerateDFS( LPCTSTR lpcszInitDir, FindFileData& findFileData, HANDLE hStopEvent /*= NULL*/ )
{
	// depth-first searching, DFS:
	// whenever we meet a folder, we dig into it
	CDFSFileFinder fileFinder(lpcszInitDir, findFileData);
	if ( !fileFinder.FindFirst() )
	{
		m_dwLastError = ::GetLastError();
		OnError(fileFinder.GetPath());
		return false;
	}
	else
	{
		while ( !fileFinder.Finish() && !IsStopEventSignaled() )
		{
			const FindFileData& fileInfo = fileFinder.GetFileInfo();
			if ( !fileFinder.IsReparsePoint() )
			{
				if ( fileFinder.IsDirectory() )
				{
					if ( !fileFinder.IsDot() )
					{
						IncreaseFindFolderCounter();
						if ( CheckUseDir(fileFinder.GetPath(), fileInfo) )
						{
							HandleDir(fileFinder.GetPath(), fileInfo);
							IncreaseAcceptedFolderCounter();
							if ( !fileFinder.FindSubDirFirst() && !OnError(fileFinder.GetPath()) )
								return false;
						}
					}
				}
				else
				{
					IncreaseFindFileCounter();
					if ( CheckUseFile(fileFinder.GetPath(), fileInfo) )
					{
						HandleFile(fileFinder.GetPath(), fileInfo);
						IncreaseAcceptedFindFileCounter();
					}
				}
			}
			// Retrace
			if ( !fileFinder.FindCurDirNext() )
			{
				do 
				{
					FinishedDir( fileFinder.GetPath() );
				} while ( !fileFinder.FindParentDirNext() && !fileFinder.Finish() && !IsStopEventSignaled() );
			}
		}
	}
	return true;
}
#endif // FILEENUMERATOR_BFS
#endif // FILEENUMERATOR_RECURSION

bool CFileEnumeratorBase::Enumerate( LPCTSTR lpcszInitDir, bool bFindSubDir /*= true*/, HANDLE hStopEvent /*= NULL*/ )
{
	if ( !lpcszInitDir || !*lpcszInitDir )
		return false;

	bool bRet		= true;

#ifdef RUN_32BIT_APP_ON_64BIT_OS
#if _MSC_VER >= 1300 && !defined(_M_X64)
	// We are writing a 32-bit application to list all the files in a directory and the application may be run on a 64-bit computer
	// See http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx for more detail
	PVOID OldValue = NULL;
	Wow64DisableWow64FsRedirection(&OldValue);
#endif
#endif // RUN_32BIT_APP_ON_64BIT_OS

	FindFileData findFileData;

#ifdef FILEENUMERATOR_RECURSION
	bRet = EnumerateRecursively(lpcszInitDir, findFileData, bFindSubDir, hStopEvent);
#else
	if ( bFindSubDir )
	{
	#ifdef FILEENUMERATOR_BFS
		bRet = EnumerateBFS(lpcszInitDir, findFileData, hStopEvent);
	#else
		bRet = EnumerateDFS(lpcszInitDir, findFileData, hStopEvent);
	#endif // FILEENUMERATOR_BFS
	}
	else
	{
		CFileFinder fileFinder(lpcszInitDir, findFileData);
		if ( !fileFinder.FindFirst() )
		{
			m_dwLastError = ::GetLastError();
			OnError(lpcszInitDir);
			bRet = false;
		}
		else
		{
			for ( ; !fileFinder.Finish() && !IsStopEventSignaled(); fileFinder.FindCurDirNext() )
			{
				const FindFileData& fileInfo = fileFinder.GetFileInfo();
				if ( !fileFinder.IsDirectory() )
				{
					if ( CheckUseFile(fileFinder.GetPath(), fileInfo) )
					{
						HandleFile(fileFinder.GetPath(), fileInfo);
						IncreaseFindFileCounter();
					}
				}
			}
		}
	}

	CFE_ASSERT( CFileFinder::ZeroInstance() );	// memory leaks?
#endif // FILEENUMERATOR_RECURSION

#ifdef RUN_32BIT_APP_ON_64BIT_OS
#if _MSC_VER >= 1300 && !defined(_M_X64)
	Wow64RevertWow64FsRedirection(OldValue);
#endif
#endif // RUN_32BIT_APP_ON_64BIT_OS
	return bRet;
}

//////////////////////////////////////////////////////////////////////
// CFilteredFileEnumerator
//////////////////////////////////////////////////////////////////////

CFilteredFileEnumerator::CFilteredFileEnumerator()
{
	
}

CFilteredFileEnumerator::~CFilteredFileEnumerator()
{
	
}

void CFilteredFileEnumerator::SetFilterPatterns( LPCTSTR lpcszFileIncPattern /*= NULL*/, 
												LPCTSTR lpcszFileExcPattern /*= NULL*/, 
												LPCTSTR lpcszDirIncPattern /*= NULL*/, 
												LPCTSTR lpcszDirExcPattern /*= NULL */ 
												)
{
	tstring strFileIncPattern = lpcszFileIncPattern ? lpcszFileIncPattern : _T("");
	tstring strFileExcPattern = lpcszFileExcPattern ? lpcszFileExcPattern : _T("");
	tstring strDirIncPattern = lpcszDirIncPattern ? lpcszDirIncPattern : _T("");
	tstring strDirExcPattern = lpcszDirExcPattern ? lpcszDirExcPattern : _T("");

	if (!strFileIncPattern.empty())
		Tokenize(m_slFileIncludePattern,  strFileIncPattern);
	if (!strFileExcPattern.empty())
		Tokenize(m_slFileExcludePattern, strFileExcPattern);
	if (!strDirIncPattern.empty())
		Tokenize(m_slDirIncludePattern,  strDirIncPattern);
	if (!strDirExcPattern.empty())
		Tokenize(m_slDirExcludePattern, strDirExcPattern);
}

bool CFilteredFileEnumerator::CheckUseFile( LPCTSTR lpcszPath, const FindFileData& ffd )
{
	tstring strFile = ffd.cFileName;
	return ( (m_slFileExcludePattern.empty() || !CompareList(m_slFileExcludePattern, strFile)) 
		&& (m_slFileIncludePattern.empty() || CompareList(m_slFileIncludePattern, strFile)) 
		);
}

bool CFilteredFileEnumerator::CheckUseDir( LPCTSTR lpcszPath, const FindFileData& ffd )
{
	tstring strFolder = ffd.cFileName;
	return ( (m_slDirExcludePattern.empty() || !CompareList(m_slDirExcludePattern, strFolder)) 
		&& (m_slDirIncludePattern.empty() || CompareList(m_slDirIncludePattern, strFolder)) 
		);
}

void CFilteredFileEnumerator::Tokenize( stringlist& plsTokenized, tstring& sPattern )
{
	// search strings are tokenized by ';' (semicolon) character
	
	tstring::size_type position = 0;
	tstring::size_type length = 0;
	
	while (true)
	{
		position = sPattern.find_first_not_of(';', length);
		length = sPattern.find_first_of(';', length + 1);
		if ( position == tstring::npos )
			break;
		
		plsTokenized.push_back(sPattern.substr(position, length - position));
	}
}

bool CompareStrings(LPCTSTR sPattern, LPCTSTR sFileName, bool bNoCase = true)
{
	TCHAR temp1[2] = _T("");
	TCHAR temp2[2] = _T("");
	LPCTSTR pStar  = 0;
	LPCTSTR pName  = 0;
	
	while(*sFileName)
	{
		switch (*sPattern)
		{
		case '?':
			++sFileName; ++sPattern;
			continue;
		case '*':
			if (!*++sPattern) return 1;
			pStar = sPattern;
			pName = sFileName + 1;
			continue;
		default:
			if(bNoCase) 
			{
				// _tcsicmp works with strings not chars !!
				*temp1 = *sFileName;
				*temp2 = *sPattern;
				if (!_tcsicmp(temp1, temp2))     // if equal
				{
					++sFileName; 
					++sPattern; 
					continue;
				}
			}
			else if (*sFileName == *sPattern)    // bNoCase == false, 
			{                                    // compare chars directly
				++sFileName; 
				++sPattern; 
				continue;
			}
			
			// chars are NOT equal, 
			// if there was no '*' thus far, strings don't match
			if(!pStar) return 0;
			
			// go back to last '*' character
			sPattern = pStar;
			sFileName = pName++;
			continue;
		}
	}
	// check is there anything left after last '*'
	while (*sPattern == '*') ++sPattern;
	return (!*sPattern);
}

bool CFilteredFileEnumerator::CompareList( stringlist& plsPattern, tstring& sFileName )
{
	stringlist::iterator iter = plsPattern.begin();
	
	for(; iter != plsPattern.end(); ++iter)
	{
		//if (str_pattern_match(iter->c_str(), sFileName.c_str()) )
		if (CompareStrings(iter->c_str(), sFileName.c_str()) )
			return true;
	}
	
	return false;
}

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions