in source/code/scxcorelib/pal/scxdirectoryinfo.cpp [335:574]
void FindFiles(const SCXCoreLib::SCXFilePath& path, const SCXCoreLib::SCXDirectorySearchOptions& options)
{
#if defined(WIN32)
struct _wfinddata_t c_file; // Directory entry. From <wchar.h>
intptr_t hFile; // Handle to directory. From <crtdefs>.h
errno_t eno; // Temporary errno storage
// Extract wide char representation of directory name from SCXFilePath and append a * wildcard
std::wstring wdirname(path.GetDirectory());
wdirname += L'*';
// Open the directory and get the handle
hFile = _wfindfirst(wdirname.c_str(), &c_file);
if( hFile == -1L ) {
// It failed. Read error code and make an exception of it.
eno = errno;
errno = 0;
// Not finding the directory is an exception
if (eno == ENOENT) {
throw SCXCoreLib::SCXFilePathNotFoundException(path.GetDirectory(), SCXSRCLOCATION);
}
wchar_t errtxt[100];
__wcserror_s(errtxt, sizeof errtxt / sizeof(wchar_t), wdirname.c_str());
if (eno == EINVAL) {
// Unlike Unix, we get EINVAL even if the dir is read protected
throw SCXCoreLib::SCXInvalidArgumentException(wdirname, errtxt, SCXSRCLOCATION);
} else {
throw SCXCoreLib::SCXResourceExhaustedException(errtxt, wdirname, SCXSRCLOCATION);
}
}
do {
// Undocumented feature: Junction Files/Reparse Points are silently skipped
if ((c_file.attrib & 0x400)) { continue; }
// Check file attributes and decide if this file should be included
bool isSystem = ((c_file.attrib & _A_SYSTEM) != 0);
// Case 1: This is a system file and those should not be listed at all
if (isSystem && 0 == (options & SCXCoreLib::eDirSearchOptionSys)) { continue; }
// Case 2: This is a subdir
if (c_file.attrib & _A_SUBDIR) {
if (0 == (options & SCXCoreLib::eDirSearchOptionDir)) { continue;} // ...and those should not be listed
// Do away with . and ..
if (c_file.name[0] == L'.'
&& (c_file.name[1] == L'\0'
|| (c_file.name[1] == L'.' && c_file.name[2] == L'\0'))) { continue; }
SCXCoreLib::SCXFilePath f(path); // Use current path and filename
f.SetFilename(L""); // Only interested in path component
f.AppendDirectory(std::wstring(c_file.name));
m_deps->Result(f, false);
}
else {
// Case 3: This an ordinay file
if (0 == (options & SCXCoreLib::eDirSearchOptionFile) && !isSystem) {continue;}// ... and those should not be listed unless being system files
SCXCoreLib::SCXFilePath f(path); // Use current path and filename
f.SetFilename(std::wstring(c_file.name));
m_deps->Result(f, false);
}
} while( _wfindnext( hFile, &c_file ) == 0 );
// Check errno but first release resources. ENOENT is ok (and expected).
_get_errno(&eno);
_findclose(hFile);
if (eno != ENOENT && eno != 0) {
// If we get here something went wrong and an exception should be thrown
wchar_t errtxt[100];
__wcserror_s(errtxt, sizeof errtxt / sizeof(wchar_t), wdirname.c_str());
if (eno == EINVAL) {
throw SCXCoreLib::SCXInvalidArgumentException(wdirname, errtxt, SCXSRCLOCATION);
} else {
throw SCXCoreLib::SCXResourceExhaustedException(errtxt, wdirname, SCXSRCLOCATION);
}
}
#else /* WIN32 */
/*
Note: This implementation is supposed to be mostly POSIX 1003.1 compliant.
Using the d_type field of the dirent structure is however not. Posix
don't require that this field is present. Instead you do a stat() call
on each file to figure out its file type. This means an extra
file access for each file in the directory. Using d_type is ok on
all BSD derived unixes such as Linux. On Solaris it isn't, and HP/UX is still unknown.
Strictly speaking, readdir() is not thread safe. But that only applies
if multiple threads access the same dirstream and that is not the case
in this code.
Right now the function is hardwired to use UTF-8 as external character set.
I would like see a method like SCXFilePath::GetLocalEncoding() that returns
a char* with the fully qualified pathname encoded in the local character set.
*/
DIR *dirp = 0; // Directory stream object. From <dirent.h>
struct dirent *dentp = 0; // Pointer to a directory entry. From <dirent.h>
int eno = 0; // Temporary storage for errno.
char filenamebuf[PATH_MAX]; // Used to construct all filenames
char *nameendptr = 0; // Position after directory name
size_t nameendsz = 0; // Remaing space in filenamebuf
size_t copysz = 0; // How many characters to strncopy to filenamebuf
errno = 0;
// Extract "char *" representation of directory name from SCXFilePath.
std::wstring wdirname(path.GetDirectory());
std::string dirname(SCXCoreLib::StrToUTF8(wdirname)); // May throw, but that's ok?
strncpy(filenamebuf, dirname.c_str(), sizeof(filenamebuf));
// In the future I expect to be able to just do the following and be done with it
// path.GetLocalEncoding(filenamebuf, sizeof(filenamebuf));
// Prepare file name buffer for reuse (every name is temporarily stored here)
nameendptr = filenamebuf + strlen(filenamebuf);
// if (nameendptr[-1] != '/') { *nameendptr = '/'; ++nameendptr; *nameendptr = '\0'; }
nameendsz = sizeof(filenamebuf) - (nameendptr - filenamebuf) - 2;
// Open the directory and get the handle
dirp = opendir(filenamebuf);
if (dirp == 0) {
// It failed. Read error code and make an exception of it.
eno = errno;
errno = 0;
if (eno == ENOENT) {
throw SCXCoreLib::SCXFilePathNotFoundException(path.GetDirectory(), SCXSRCLOCATION);
}
std::string errtxt(SCXCoreLib::strerror(eno));
if (eno == EACCES || eno == ENOTDIR) {
throw SCXCoreLib::SCXInvalidArgumentException(wdirname, SCXCoreLib::StrFromUTF8(errtxt), SCXSRCLOCATION);
} else {
// All other errno's are resource related. Memory, descriptors, etc.
// Note that the parameters supplied to the exception are not a very good
// match to what it expects.
throw SCXCoreLib::SCXResourceExhaustedException(SCXCoreLib::StrFromUTF8(errtxt), wdirname, SCXSRCLOCATION);
}
}
// Read a directory entry
struct dirent *currentDir = NULL;
int len_entry = offsetof(struct dirent, d_name) + PATH_MAX + 1;
currentDir = (struct dirent *)malloc(len_entry);
#if defined(sun)
while (NULL != (dentp = readdir_r(dirp, currentDir))) {
#else
while ((0 == readdir_r(dirp, currentDir, &dentp)) && NULL != dentp) {
#endif
bool isdir = false; // Is this a directory entry
SCXCoreLib::SCXFileSystem::SCXStatStruct* pStat = 0;
// Only BSD-based C-libraries have the d_type entry in the DIR structure
// In all others we must always do an explict stat call to find out the type
#if defined(linux)
// Check file type and decide if this file should be included
switch (dentp->d_type) {
case DT_LNK: /* Always dereference symlinks */
case DT_UNKNOWN: /* File type can't be found out from dirent. Do a stat() call. */
#endif
/* Decide how many characters to copy. (We do this because the buffer is big)*/
copysz = MIN(nameendsz, static_cast<size_t>(_D_ALLOC_NAMLEN(dentp)));
strncpy(nameendptr, dentp->d_name, copysz);
pStat = m_deps->DoStat(filenamebuf);
if (0 == pStat) {
/* We must account for links that point to non-existing files */
if (errno == ENOENT) { errno = 0; continue; }
else { goto after; }
}
if (S_ISREG(pStat->st_mode)) {
if (0 == (options & SCXCoreLib::eDirSearchOptionFile)) { continue; }
isdir = false;
} else if (S_ISDIR(pStat->st_mode)) {
if (0 == (options & SCXCoreLib::eDirSearchOptionDir)) { continue; }
isdir = true;
}
else {
if (0 == (options & SCXCoreLib::eDirSearchOptionSys)) { continue; }
}
#if defined(linux)
break;
case DT_REG:
// This is a regular file
if (0 == (options & SCXCoreLib::eDirSearchOptionFile)) { continue; }
isdir = false;
break;
case DT_DIR:
// This is a directory
if (0 == (options & SCXCoreLib::eDirSearchOptionDir)) { continue; }
isdir = true;
break;
default:
// This is a "system file" (Device, socket, etc.)
if (0 == (options & SCXCoreLib::eDirSearchOptionSys)) { continue; }
}
#endif
if (isdir) {
// Do away with . and ..
if (dentp->d_name[0] == '.'
&& (dentp->d_name[1] == '\0'
|| (dentp->d_name[1] == '.' && dentp->d_name[2] == '\0'))) { continue;}
}
// make new SCXFilePath structure with current path and filename, add to result
std::string dname(dentp->d_name);
std::wstring wdname(SCXCoreLib::StrFromUTF8(dname));
SCXCoreLib::SCXFilePath f(path); // Use current path and filename
if (isdir) {
f.SetFilename(L""); // Only interested in path component
f.AppendDirectory(wdname);
} else { // Ordinary file or system
f.SetFilename(wdname);
}
m_deps->Result(f, pStat != 0);
}
after:
if (currentDir)
{
free(currentDir);
}
eno = errno;
errno = 0;
closedir(dirp);
if (eno != 0) {
throw SCXCoreLib::SCXErrnoException(L"stat", eno, SCXSRCLOCATION);
}
#endif
}
private:
SCXCoreLib::SCXHandle<SCXDirectoryEnumeratorBehaviour> m_deps; //!< Dependency object.
};