FILEENUM.CXX

//+------------------------------------------------------------------ 
//
// Copyright (C) 1995, Microsoft Corporation.
//
// File: FileEnum.cxx
//
// Contents: class encapsulating file enumeration, including a deep option
//
// Classes: CFileEnumeration
//
// History: Nov-93 DaveMont Created.
//
//-------------------------------------------------------------------

#include <t2.hxx>
#include <FileEnum.hxx>
#if DBG
extern ULONG Debug;
#endif
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::CFileEnumerate, public
//
// Synopsis: initializes data members, constructor will not throw
//
// Arguments: IN [fdeep] - TRUE = go into sub-directories
//
//----------------------------------------------------------------------------
CFileEnumerate::CFileEnumerate(BOOL fdeep)
: _fdeep(fdeep),
_findeep(FALSE),
_froot(FALSE),
_fcannotaccess(FALSE),
_pcfe(NULL),
_pwfileposition(NULL),
_handle(INVALID_HANDLE_VALUE)
{
ENUMERATE_RETURNS((stderr, "CFileEnumerate ctor\n"))
}
//+---------------------------------------------------------------------------
//
// Member: Dtor, public
//
// Synopsis: closes handles
//
// Arguments: none
//
//----------------------------------------------------------------------------
CFileEnumerate::~CFileEnumerate()
{
if (_handle != INVALID_HANDLE_VALUE)
FindClose(_handle);
ENUMERATE_RETURNS((stderr, "CFileEnumerate dtor (%ws)\n", _wpath))
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::Init, public
//
// Synopsis: Init must be called before any other methods - this
// is not enforced. converts a ASCII file/path to a UNICODE
// file/path, and gets the first file in the enumeration
//
// Arguments: IN [filename] - the path/file to enumerate
// OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::Init(CHAR *filename, WCHAR **wfilename, BOOL *fdir)
{
// Initialize the file name

if (filename && (strlen(filename) < MAX_PATH))
{
// make it wchar
WCHAR winfilename[MAX_PATH];

if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
filename, -1,
winfilename, sizeof(winfilename)) == 0)
return(ERROR_INVALID_NAME);

// finish initialization

return(_ialize(winfilename, wfilename, fdir));
}
ENUMERATE_FAIL((stderr, "Init bad file name: %ld\n",ERROR_INVALID_NAME))
return(ERROR_INVALID_NAME);
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::Init, public
//
// Synopsis: Same as previous, except takes UNICODE file/path as input
//
// Arguments: IN [filename] - the path/file to enumerate
// OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::Init(WCHAR *filename, WCHAR **wfilename, BOOL *fdir)
{
// Initialize the file name

if (filename && (wcslen(filename) < MAX_PATH))
{
return(_ialize(filename, wfilename, fdir));
}
ENUMERATE_FAIL((stderr, "Init bad file name: %ld\n",ERROR_INVALID_NAME))
return(ERROR_INVALID_NAME);
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::_ialize, private
//
// Synopsis: finishes initialization and starts search for first file in
// the enumeration
//
// Arguments: OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::_ialize(WCHAR *winfilename, WCHAR **wfilename, BOOL *fdir)
{
ENUMERATE_RETURNS((stderr, "Init start, path = %ws\n", winfilename))
ULONG ret = ERROR_SUCCESS;

ENUMERATE_STAT((stderr, "start path = %ws\n",winfilename))

// save the location of the filename or wildcards

ULONG cwcharcount;

if (!(cwcharcount = GetFullPathName(winfilename,
MAX_PATH,
_wpath,
&_pwfileposition)))
{
return(ERROR_INVALID_NAME);
}

ENUMERATE_STAT((stderr, "got full path name = %ws, filename = (%ws), total chars = %d\n",_wpath, _pwfileposition, cwcharcount))

// if the filepart (_pwfileposition) is NULL, then the name must end in a slash.
// add a *

if (NULL == _pwfileposition)
{
_pwfileposition = (WCHAR *)Add2Ptr(_wpath,wcslen(_wpath)*sizeof(WCHAR));
}

// save the filename/wildcards

wcscpy(_wwildcards, _pwfileposition);

ENUMERATE_EXTRA((stderr, "wild cards = %ws\n",_wwildcards))

// if we are at a root (path ends in :\)

if ( (_wpath[wcslen(_wpath) - 1] == L'\\') &&
(wcslen(_wpath) > 1) &&
(_wpath[wcslen(_wpath) - 2] == L':') )
{
_wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
_wfd.cFileName[0] = L'\0';
*wfilename = _wpath;
*fdir = TRUE;
_froot = TRUE;
} else
{
// check to see if we can iterate through files
if ( (INVALID_HANDLE_VALUE == ( _handle = FindFirstFile(_wpath, &_wfd) ) ) )
{
ret = GetLastError();
_fcannotaccess = (ERROR_ACCESS_DENIED == ret);

ENUMERATE_FAIL((stderr, "find first returned: %ld\n",ret))
}
if (ERROR_SUCCESS == ret)
{
// reject . & .. filenames (go on to next file )

if ( (0 == wcscmp(_wfd.cFileName, L".")) ||
(0 == wcscmp(_wfd.cFileName, L"..")) )
{
ret = _NextLocal(wfilename,fdir);
} else
{
// return the current directory

if (_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
*fdir = TRUE;
else
*fdir = FALSE;

// add the filename to the path so the whole thing is returned

wcscpy(_pwfileposition, _wfd.cFileName);

*wfilename = _wpath;
}
}

ENUMERATE_STAT((stderr, "next filename = %ws\n", *wfilename))
}

// if we are going deep and we did not find a file yet:

if ( _fdeep && ( ( ERROR_NO_MORE_FILES == ret ) ||
( ERROR_FILE_NOT_FOUND == ret ) ) )
{
if (_handle != INVALID_HANDLE_VALUE)
{
FindClose(_handle);
_handle = INVALID_HANDLE_VALUE;
}
ret = _InitDir(wfilename, fdir);
}

ENUMERATE_RETURNS((stderr, "Init returning = %ws(%ld)\n\n", *wfilename, ret))
return(ret);
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::Next, public
//
// Synopsis: finds the next file in the enumeration
//
// Arguments: OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::Next(WCHAR **wfilename, BOOL *fdir)
{
ENUMERATE_RETURNS((stderr, "Next start, path = %ws\n", _wpath))
ULONG ret = ERROR_NO_MORE_FILES;

// if we failed to initialize with an ERROR_ACCESS_DENIED, then exit
if (_fcannotaccess)
return(ERROR_NO_MORE_FILES);

// if we are not in deep

if (!_findeep)
{
if (!_froot)
ret = _NextLocal(wfilename, fdir);

// if we ran out of files and we are going deep:

if ( _fdeep &&
( ( ERROR_NO_MORE_FILES == ret ) ||
( ERROR_FILE_NOT_FOUND == ret ) || _froot ) )
{
if (_handle != INVALID_HANDLE_VALUE)
{
FindClose(_handle);
_handle = INVALID_HANDLE_VALUE;
}
ret = _InitDir(wfilename, fdir);
_froot = FALSE; // (we are past the root now)
}

} else
{
// if we are already down a directory (and in deep)

if (_pcfe)
{
if (ERROR_SUCCESS != (ret = _pcfe->Next(wfilename, fdir)))
{
if (ERROR_ACCESS_DENIED != ret)
{
delete _pcfe;
_pcfe = NULL;
}
}
}

// we need to go to the next directory in the current dir

if (ERROR_NO_MORE_FILES == ret)
{
ret = _NextDir(wfilename, fdir);
}
}
ENUMERATE_RETURNS((stderr, "Next returning = %ws(%ld)\n\n", *wfilename, ret))
return(ret);
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::_NextLocal, private
//
// Synopsis: searchs for the next file in the current directory
//
// Arguments: OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::_NextLocal(WCHAR **wfilename, BOOL *fdir)
{
ENUMERATE_RETURNS((stderr, "_NextLocal start, path = %ws\n", _wpath))
ULONG ret = ERROR_SUCCESS;

// ensure that we have a valid handle for a findnextfile

if (INVALID_HANDLE_VALUE == _handle)
{
ret = ERROR_INVALID_HANDLE;
} else
{
do
{
if (!FindNextFile(_handle, &_wfd))
{
ret = GetLastError();
ENUMERATE_FAIL((stderr, "find next returned: %ld\n",ret))
} else
ret = ERROR_SUCCESS;
}
while ( (ERROR_SUCCESS == ret) &&
( (0 == wcscmp(_wfd.cFileName, L".")) ||
(0 == wcscmp(_wfd.cFileName, L"..")) ) );


// if we found a file

if (ERROR_SUCCESS == ret)
{
// return the directory attrib.

if (_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
*fdir = TRUE;
else
*fdir = FALSE;

// add the filename to the path so the whole thing is returned

wcscpy(_pwfileposition, _wfd.cFileName);

*wfilename = _wpath;

ENUMERATE_STAT((stderr, "next filename = %ws\n", *wfilename))
}
}
ENUMERATE_RETURNS((stderr, "_NextLocal returning = %ws(%ld)\n", *wfilename, ret))

return(ret);
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::_InitDir, private
//
// Synopsis: (only called if going deep)
// goes down a directory (and thus causing a new CFileEnumerator
// to be created, or re-initializies
//
// Arguments: OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::_InitDir(WCHAR **wfilename, BOOL *fdir)
{
ENUMERATE_RETURNS((stderr, "_InitDir start, path = %ws\n", _wpath))
ULONG ret = ERROR_SUCCESS;

// check and see if a directory was entered as the filename

if ( (0 == _wcsicmp(_wwildcards, _wfd.cFileName)) &&
(_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
ENUMERATE_EXTRA((stderr, "first file matched directory = %ws\n", _wpath))
_pwfileposition += wcslen(_wfd.cFileName);
wcscpy(_pwfileposition, L"\\*.*");
_pwfileposition++;
wcscpy(_wwildcards, L"*.*");
ENUMERATE_EXTRA((stderr, " path = %ws\n",_wpath))
ENUMERATE_EXTRA((stderr, "wild cards = %ws\n",_wwildcards))

WCHAR winfilename[MAX_PATH];
wcscpy(winfilename, _wpath);

ret = _ialize(winfilename, wfilename, fdir);
} else
{

// we are in deep

_findeep = TRUE;

// search thru all directories

wcscpy(_pwfileposition, L"*.*");

if (INVALID_HANDLE_VALUE == ( _handle = FindFirstFile(_wpath, &_wfd) ))
{
ret = GetLastError();
ENUMERATE_FAIL((stderr, "find first (dir) returned: %ld\n",ret))
} else
{
if ( !(_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
(0 == wcscmp(_wfd.cFileName, L".")) ||
(0 == wcscmp(_wfd.cFileName, L"..")) )
{
ret = _NextDir(wfilename, fdir);
} else
{
// if we have a sub directory, go down it

ret = _DownDir(wfilename, fdir);

// if we found nothing in that first sub directory, go the the next one

if ( (ERROR_NO_MORE_FILES == ret ) ||
(ERROR_FILE_NOT_FOUND == ret ) )
{
ret = _NextDir(wfilename, fdir);
}
}
}
}
ENUMERATE_RETURNS((stderr, "_InitDir returning = %ws(%ld)\n", *wfilename, ret))

return(ret);
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::_NextDir, private
//
// Synopsis: (only called if going deep)
// finds the next sub-directory from the current directory,
// and then goes down into that directory
//
// Arguments: OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::_NextDir(WCHAR **wfilename, BOOL *fdir)
{
ENUMERATE_RETURNS((stderr, "_NextDir start, path = %ws\n", _wpath))
ULONG ret = ERROR_SUCCESS;

// skip the . & .. & files we cannot access

if (INVALID_HANDLE_VALUE == _handle)
{
ret = ERROR_INVALID_HANDLE;
} else
{
do
{
do
{
if (!FindNextFile(_handle, &_wfd))
{
ret = GetLastError();
ENUMERATE_FAIL((stderr, "find next returned: %ld\n",ret))
} else
ret = ERROR_SUCCESS;
}
while ( (ERROR_SUCCESS == ret) &&
( !(_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
(0 == wcscmp(_wfd.cFileName, L".")) ||
(0 == wcscmp(_wfd.cFileName, L"..")) ) );

// if we found a directory

if (ERROR_SUCCESS == ret)
{
ret = _DownDir(wfilename, fdir);
} else
{
// out of subdirectories to search, break out of the loop
break;
}
}
while (( ERROR_NO_MORE_FILES == ret) || (ERROR_FILE_NOT_FOUND == ret));
}
ENUMERATE_RETURNS((stderr, "_NextDir returning = %ws(%ld)\n", *wfilename, ret))

return(ret);
}
//+---------------------------------------------------------------------------
//
// Member: CFileEnumerate::_DownDir, private
//
// Synopsis: (only called if going deep)
// creates a new CFileEnumerator for a sub-directory
//
// Arguments: OUT [wfilename] - first file in the enumeration
// OUT [fdir] - TRUE = returned file is a directory
//
//----------------------------------------------------------------------------
ULONG CFileEnumerate::_DownDir(WCHAR **wfilename, BOOL *fdir)
{
ENUMERATE_RETURNS((stderr, "_DownDir start, path = %ws\n", _wpath))
ULONG ret;

// make a new file enumerator class (this one) We should only go down
// 8 directories at most.

_pcfe = new CFileEnumerate(_fdeep);

// add the wildcards to the end of the directory we are going down

wcscpy(_pwfileposition, _wfd.cFileName);
wcscat(_pwfileposition, L"\\");
wcscat(_pwfileposition, _wwildcards);

// start it up and see if we find a match

if (ERROR_SUCCESS != (ret = _pcfe->Init(_wpath, wfilename, fdir)))
{
if (ERROR_ACCESS_DENIED != ret)
{
delete _pcfe;
_pcfe = NULL;
}
}
ENUMERATE_RETURNS((stderr, "_DownDir returning = %ws(%ld)\n", *wfilename, ret))
return(ret);
}